claude-mpm 4.6.0__py3-none-any.whl → 4.7.0__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 +206 -48
- claude_mpm/agents/BASE_PROMPT_ENGINEER.md +787 -0
- claude_mpm/agents/base_agent_loader.py +3 -1
- claude_mpm/agents/templates/engineer.json +10 -4
- claude_mpm/agents/templates/prompt-engineer.json +517 -87
- claude_mpm/cli/commands/cleanup.py +1 -1
- claude_mpm/cli/commands/mcp_setup_external.py +2 -2
- claude_mpm/cli/commands/memory.py +1 -1
- claude_mpm/cli/commands/mpm_init.py +5 -4
- claude_mpm/cli/commands/run.py +4 -4
- claude_mpm/cli/shared/argument_patterns.py +18 -11
- claude_mpm/cli/shared/base_command.py +1 -1
- claude_mpm/config/experimental_features.py +3 -3
- claude_mpm/config/socketio_config.py +1 -1
- claude_mpm/core/cache.py +2 -2
- claude_mpm/core/claude_runner.py +5 -7
- claude_mpm/core/container.py +10 -4
- claude_mpm/core/file_utils.py +10 -8
- claude_mpm/core/framework/formatters/context_generator.py +3 -2
- claude_mpm/core/framework/loaders/agent_loader.py +11 -7
- claude_mpm/core/injectable_service.py +11 -8
- claude_mpm/core/interactive_session.py +5 -4
- claude_mpm/core/oneshot_session.py +3 -2
- claude_mpm/core/pm_hook_interceptor.py +15 -9
- claude_mpm/core/unified_paths.py +6 -5
- claude_mpm/dashboard/api/simple_directory.py +16 -17
- claude_mpm/hooks/claude_hooks/event_handlers.py +3 -2
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +2 -2
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +2 -2
- claude_mpm/hooks/claude_hooks/installer.py +10 -10
- claude_mpm/hooks/claude_hooks/response_tracking.py +3 -2
- claude_mpm/hooks/claude_hooks/services/state_manager.py +3 -2
- claude_mpm/hooks/tool_call_interceptor.py +6 -3
- claude_mpm/models/agent_session.py +3 -1
- claude_mpm/scripts/mcp_server.py +3 -5
- claude_mpm/services/agents/agent_builder.py +4 -4
- claude_mpm/services/agents/deployment/deployment_type_detector.py +10 -14
- claude_mpm/services/agents/deployment/local_template_deployment.py +6 -3
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +15 -11
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +9 -6
- claude_mpm/services/agents/loading/agent_profile_loader.py +1 -2
- claude_mpm/services/agents/memory/agent_memory_manager.py +27 -27
- claude_mpm/services/agents/memory/content_manager.py +9 -4
- claude_mpm/services/claude_session_logger.py +5 -8
- claude_mpm/services/cli/memory_crud_service.py +1 -1
- claude_mpm/services/cli/memory_output_formatter.py +1 -1
- claude_mpm/services/cli/startup_checker.py +13 -10
- claude_mpm/services/cli/unified_dashboard_manager.py +10 -6
- claude_mpm/services/command_deployment_service.py +9 -7
- claude_mpm/services/core/path_resolver.py +8 -5
- claude_mpm/services/diagnostics/checks/agent_check.py +4 -7
- claude_mpm/services/diagnostics/checks/installation_check.py +19 -16
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +30 -28
- claude_mpm/services/diagnostics/checks/startup_log_check.py +5 -3
- claude_mpm/services/events/core.py +2 -3
- claude_mpm/services/framework_claude_md_generator/content_validator.py +2 -2
- claude_mpm/services/hook_installer_service.py +2 -3
- claude_mpm/services/hook_service.py +5 -6
- claude_mpm/services/mcp_gateway/auto_configure.py +4 -5
- claude_mpm/services/mcp_gateway/main.py +7 -4
- claude_mpm/services/mcp_gateway/server/stdio_server.py +3 -4
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -2
- claude_mpm/services/mcp_service_verifier.py +18 -17
- claude_mpm/services/memory/builder.py +1 -2
- claude_mpm/services/memory/indexed_memory.py +1 -1
- claude_mpm/services/memory/optimizer.py +1 -2
- claude_mpm/services/monitor/daemon_manager.py +3 -3
- claude_mpm/services/monitor/handlers/file.py +5 -4
- claude_mpm/services/monitor/management/lifecycle.py +1 -1
- claude_mpm/services/monitor/server.py +14 -12
- claude_mpm/services/project/architecture_analyzer.py +5 -5
- claude_mpm/services/project/metrics_collector.py +4 -4
- claude_mpm/services/project/project_organizer.py +4 -4
- claude_mpm/services/project/registry.py +9 -3
- claude_mpm/services/shared/config_service_base.py +2 -3
- claude_mpm/services/socketio/handlers/file.py +5 -4
- claude_mpm/services/socketio/handlers/git.py +7 -7
- claude_mpm/services/socketio/server/core.py +10 -10
- claude_mpm/services/subprocess_launcher_service.py +5 -10
- claude_mpm/services/ticket_services/formatter_service.py +1 -1
- claude_mpm/services/ticket_services/validation_service.py +5 -5
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +5 -5
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +4 -4
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +4 -4
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +4 -4
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +4 -4
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +6 -2
- claude_mpm/services/unified/config_strategies/unified_config_service.py +24 -13
- claude_mpm/services/version_control/conflict_resolution.py +6 -2
- claude_mpm/services/version_control/git_operations.py +1 -1
- claude_mpm/services/version_control/version_parser.py +1 -1
- claude_mpm/storage/state_storage.py +3 -3
- claude_mpm/tools/__main__.py +1 -1
- claude_mpm/tools/code_tree_analyzer.py +17 -14
- claude_mpm/tools/socketio_debug.py +7 -7
- claude_mpm/utils/common.py +6 -2
- claude_mpm/utils/config_manager.py +9 -3
- claude_mpm/utils/database_connector.py +4 -4
- claude_mpm/utils/dependency_strategies.py +1 -1
- claude_mpm/utils/environment_context.py +3 -2
- claude_mpm/utils/file_utils.py +1 -2
- claude_mpm/utils/path_operations.py +3 -1
- claude_mpm/utils/robust_installer.py +3 -4
- claude_mpm/validation/frontmatter_validator.py +4 -4
- {claude_mpm-4.6.0.dist-info → claude_mpm-4.7.0.dist-info}/METADATA +1 -1
- {claude_mpm-4.6.0.dist-info → claude_mpm-4.7.0.dist-info}/RECORD +111 -110
- {claude_mpm-4.6.0.dist-info → claude_mpm-4.7.0.dist-info}/WHEEL +0 -0
- {claude_mpm-4.6.0.dist-info → claude_mpm-4.7.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.6.0.dist-info → claude_mpm-4.7.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.6.0.dist-info → claude_mpm-4.7.0.dist-info}/top_level.txt +0 -0
@@ -11,7 +11,7 @@ Created: 2025-01-26
|
|
11
11
|
|
12
12
|
import fnmatch
|
13
13
|
from pathlib import Path
|
14
|
-
from typing import Any, Dict, List, Optional, Tuple
|
14
|
+
from typing import Any, ClassVar, Dict, List, Optional, Tuple
|
15
15
|
|
16
16
|
from claude_mpm.core.logging_utils import get_logger
|
17
17
|
|
@@ -38,7 +38,7 @@ class StructureAnalyzerStrategy(AnalyzerStrategy):
|
|
38
38
|
"""
|
39
39
|
|
40
40
|
# Common project patterns
|
41
|
-
PROJECT_PATTERNS = {
|
41
|
+
PROJECT_PATTERNS: ClassVar[dict] = {
|
42
42
|
"mvc": {
|
43
43
|
"dirs": ["models", "views", "controllers"],
|
44
44
|
"confidence": 0.8,
|
@@ -62,7 +62,7 @@ class StructureAnalyzerStrategy(AnalyzerStrategy):
|
|
62
62
|
}
|
63
63
|
|
64
64
|
# Language-specific structure patterns
|
65
|
-
LANGUAGE_STRUCTURES = {
|
65
|
+
LANGUAGE_STRUCTURES: ClassVar[dict] = {
|
66
66
|
"python": {
|
67
67
|
"src_patterns": ["src", "lib", "app"],
|
68
68
|
"test_patterns": ["tests", "test", "spec"],
|
@@ -90,7 +90,7 @@ class StructureAnalyzerStrategy(AnalyzerStrategy):
|
|
90
90
|
}
|
91
91
|
|
92
92
|
# Common ignore patterns
|
93
|
-
IGNORE_PATTERNS = [
|
93
|
+
IGNORE_PATTERNS: ClassVar[list] = [
|
94
94
|
"*.pyc",
|
95
95
|
"__pycache__",
|
96
96
|
".git",
|
@@ -10,7 +10,7 @@ from dataclasses import dataclass, field
|
|
10
10
|
from datetime import datetime
|
11
11
|
from enum import Enum
|
12
12
|
from pathlib import Path
|
13
|
-
from typing import Any, Callable, Dict, List, Optional, Type, Union
|
13
|
+
from typing import Any, Callable, ClassVar, Dict, List, Optional, Type, Union
|
14
14
|
|
15
15
|
from claude_mpm.core.logging_utils import get_logger
|
16
16
|
|
@@ -105,7 +105,7 @@ class BaseErrorHandler(ABC):
|
|
105
105
|
class FileIOErrorHandler(BaseErrorHandler):
|
106
106
|
"""Handles file I/O errors - consolidates 18 file error patterns"""
|
107
107
|
|
108
|
-
ERROR_MAPPING = {
|
108
|
+
ERROR_MAPPING: ClassVar[dict] = {
|
109
109
|
FileNotFoundError: "File not found",
|
110
110
|
PermissionError: "Permission denied",
|
111
111
|
IsADirectoryError: "Path is a directory",
|
@@ -241,7 +241,7 @@ class FileIOErrorHandler(BaseErrorHandler):
|
|
241
241
|
class ParsingErrorHandler(BaseErrorHandler):
|
242
242
|
"""Handles parsing errors - consolidates 22 parsing error patterns"""
|
243
243
|
|
244
|
-
PARSER_ERRORS = {
|
244
|
+
PARSER_ERRORS: ClassVar[dict] = {
|
245
245
|
json.JSONDecodeError: ErrorCategory.PARSING,
|
246
246
|
ValueError: ErrorCategory.PARSING, # Common for parsing
|
247
247
|
SyntaxError: ErrorCategory.PARSING,
|
@@ -559,7 +559,7 @@ class ValidationErrorHandler(BaseErrorHandler):
|
|
559
559
|
class NetworkErrorHandler(BaseErrorHandler):
|
560
560
|
"""Handles network-related errors - consolidates 12 network error patterns"""
|
561
561
|
|
562
|
-
NETWORK_ERRORS = [
|
562
|
+
NETWORK_ERRORS: ClassVar[list] = [
|
563
563
|
ConnectionError,
|
564
564
|
TimeoutError,
|
565
565
|
ConnectionRefusedError,
|
@@ -64,13 +64,17 @@ class BaseFileLoader(ABC):
|
|
64
64
|
def _read_file(self, path: Path, encoding: str = "utf-8") -> str:
|
65
65
|
"""Read file with proper error handling"""
|
66
66
|
try:
|
67
|
-
with
|
67
|
+
with Path(path).open(
|
68
|
+
encoding=encoding,
|
69
|
+
) as f:
|
68
70
|
return f.read()
|
69
71
|
except UnicodeDecodeError:
|
70
72
|
# Try with different encodings
|
71
73
|
for enc in ["latin-1", "cp1252", "utf-16"]:
|
72
74
|
try:
|
73
|
-
with
|
75
|
+
with Path(path).open(
|
76
|
+
encoding=enc,
|
77
|
+
) as f:
|
74
78
|
self.logger.warning(
|
75
79
|
f"Read {path} with fallback encoding: {enc}"
|
76
80
|
)
|
@@ -254,9 +254,10 @@ class UnifiedConfigService:
|
|
254
254
|
# Apply specified validators or use schema-defined ones
|
255
255
|
if validators:
|
256
256
|
for validator_name in validators:
|
257
|
-
if validator_name in self._validators
|
258
|
-
|
259
|
-
|
257
|
+
if validator_name in self._validators and not self._validators[
|
258
|
+
validator_name
|
259
|
+
](config, schema):
|
260
|
+
return False
|
260
261
|
else:
|
261
262
|
# Use schema to determine validators
|
262
263
|
return self._validate_schema(config, schema)
|
@@ -563,9 +564,12 @@ class UnifiedConfigService:
|
|
563
564
|
"""Validate enum values"""
|
564
565
|
properties = schema.get("properties", {})
|
565
566
|
for key, value in config.items():
|
566
|
-
if
|
567
|
-
|
568
|
-
|
567
|
+
if (
|
568
|
+
key in properties
|
569
|
+
and "enum" in properties[key]
|
570
|
+
and value not in properties[key]["enum"]
|
571
|
+
):
|
572
|
+
return False
|
569
573
|
return True
|
570
574
|
|
571
575
|
def _validate_schema(self, config: Dict[str, Any], schema: Dict) -> bool:
|
@@ -596,9 +600,13 @@ class UnifiedConfigService:
|
|
596
600
|
"""Validate unique values in arrays"""
|
597
601
|
properties = schema.get("properties", {})
|
598
602
|
for key, value in config.items():
|
599
|
-
if
|
600
|
-
|
601
|
-
|
603
|
+
if (
|
604
|
+
key in properties
|
605
|
+
and properties[key].get("uniqueItems")
|
606
|
+
and isinstance(value, list)
|
607
|
+
and len(value) != len(set(map(str, value)))
|
608
|
+
):
|
609
|
+
return False
|
602
610
|
return True
|
603
611
|
|
604
612
|
def _validate_format(self, config: Dict[str, Any], schema: Dict) -> bool:
|
@@ -641,10 +649,13 @@ class UnifiedConfigService:
|
|
641
649
|
"""Recursively validate nested structures"""
|
642
650
|
properties = schema.get("properties", {})
|
643
651
|
for key, value in config.items():
|
644
|
-
if
|
645
|
-
|
646
|
-
|
647
|
-
|
652
|
+
if (
|
653
|
+
key in properties
|
654
|
+
and isinstance(value, dict)
|
655
|
+
and "properties" in properties[key]
|
656
|
+
and not self._validate_schema(value, properties[key])
|
657
|
+
):
|
658
|
+
return False
|
648
659
|
return True
|
649
660
|
|
650
661
|
def _validate_cross_field(self, config: Dict[str, Any], schema: Dict) -> bool:
|
@@ -274,7 +274,9 @@ class ConflictResolutionManager:
|
|
274
274
|
)
|
275
275
|
|
276
276
|
try:
|
277
|
-
with
|
277
|
+
with Path(full_path).open(
|
278
|
+
encoding="utf-8",
|
279
|
+
) as f:
|
278
280
|
content = f.read()
|
279
281
|
|
280
282
|
# Parse conflict markers
|
@@ -587,7 +589,9 @@ class ConflictResolutionManager:
|
|
587
589
|
|
588
590
|
try:
|
589
591
|
# Read current content
|
590
|
-
with
|
592
|
+
with Path(file_path).open(
|
593
|
+
encoding="utf-8",
|
594
|
+
) as f:
|
591
595
|
content = f.read()
|
592
596
|
|
593
597
|
# Apply resolution strategy
|
@@ -333,7 +333,7 @@ class GitOperationsManager:
|
|
333
333
|
"""
|
334
334
|
try:
|
335
335
|
# Convert to relative path if absolute
|
336
|
-
if
|
336
|
+
if Path(file_path).is_absolute():
|
337
337
|
try:
|
338
338
|
file_path = os.path.relpath(file_path, self.project_root)
|
339
339
|
except ValueError:
|
@@ -113,7 +113,7 @@ class EnhancedVersionParser:
|
|
113
113
|
r"(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9\-\.]+))?(?:\+([a-zA-Z0-9\-\.]+))?"
|
114
114
|
)
|
115
115
|
self._changelog_version_pattern = re.compile(
|
116
|
-
r"##\s*\[?([0-9]+\.[0-9]+\.[0-9]+[^\]]*)\]?\s*[
|
116
|
+
r"##\s*\[?([0-9]+\.[0-9]+\.[0-9]+[^\]]*)\]?\s*[--]\s*(\d{4}-\d{2}-\d{2})?"
|
117
117
|
)
|
118
118
|
|
119
119
|
def _get_cached(self, key: str) -> Optional[any]:
|
@@ -268,7 +268,7 @@ class StateStorage:
|
|
268
268
|
finally:
|
269
269
|
# Clean up temp file if it still exists
|
270
270
|
if Path(temp_path).exists():
|
271
|
-
|
271
|
+
Path(temp_path).unlink()
|
272
272
|
|
273
273
|
except Exception as e:
|
274
274
|
self.logger.error(f"Atomic write failed for {file_path}: {e}")
|
@@ -288,14 +288,14 @@ class StateStorage:
|
|
288
288
|
"""
|
289
289
|
if not self.supports_locking:
|
290
290
|
# No locking on Windows
|
291
|
-
with open(
|
291
|
+
with Path(file_path).open(mode) as f:
|
292
292
|
yield f
|
293
293
|
return
|
294
294
|
|
295
295
|
# Unix-like systems with fcntl
|
296
296
|
f = None
|
297
297
|
try:
|
298
|
-
f = open(
|
298
|
+
f = Path(file_path).open(mode)
|
299
299
|
|
300
300
|
# Try to acquire lock (non-blocking)
|
301
301
|
max_attempts = 50
|
claude_mpm/tools/__main__.py
CHANGED
@@ -118,7 +118,7 @@ def main():
|
|
118
118
|
output_file = None
|
119
119
|
if args.output_file:
|
120
120
|
try:
|
121
|
-
output_file =
|
121
|
+
output_file = Path(args.output_file).open("w")
|
122
122
|
except Exception as e:
|
123
123
|
emit_json_event(
|
124
124
|
"code:analysis:error", {"message": f"Failed to open output file: {e}"}
|
@@ -21,7 +21,7 @@ import time
|
|
21
21
|
from dataclasses import dataclass
|
22
22
|
from datetime import timezone
|
23
23
|
from pathlib import Path
|
24
|
-
from typing import Any, Dict, List, Optional
|
24
|
+
from typing import Any, ClassVar, Dict, List, Optional
|
25
25
|
|
26
26
|
try:
|
27
27
|
import pathspec
|
@@ -31,14 +31,13 @@ except ImportError:
|
|
31
31
|
PATHSPEC_AVAILABLE = False
|
32
32
|
pathspec = None
|
33
33
|
|
34
|
-
|
34
|
+
import importlib.util
|
35
|
+
|
36
|
+
if importlib.util.find_spec("tree_sitter"):
|
35
37
|
import tree_sitter
|
36
|
-
import tree_sitter_javascript
|
37
|
-
import tree_sitter_python
|
38
|
-
import tree_sitter_typescript
|
39
38
|
|
40
39
|
TREE_SITTER_AVAILABLE = True
|
41
|
-
|
40
|
+
else:
|
42
41
|
TREE_SITTER_AVAILABLE = False
|
43
42
|
tree_sitter = None
|
44
43
|
|
@@ -54,7 +53,7 @@ class GitignoreManager:
|
|
54
53
|
"""
|
55
54
|
|
56
55
|
# Default patterns that should always be ignored
|
57
|
-
DEFAULT_PATTERNS = [
|
56
|
+
DEFAULT_PATTERNS: ClassVar[list] = [
|
58
57
|
".git/",
|
59
58
|
"__pycache__/",
|
60
59
|
"*.pyc",
|
@@ -88,13 +87,13 @@ class GitignoreManager:
|
|
88
87
|
]
|
89
88
|
|
90
89
|
# Additional patterns to hide dotfiles (when enabled)
|
91
|
-
DOTFILE_PATTERNS = [
|
90
|
+
DOTFILE_PATTERNS: ClassVar[list] = [
|
92
91
|
".*", # All dotfiles
|
93
92
|
".*/", # All dot directories
|
94
93
|
]
|
95
94
|
|
96
95
|
# Important files/directories to always show
|
97
|
-
DOTFILE_EXCEPTIONS = {
|
96
|
+
DOTFILE_EXCEPTIONS: ClassVar[set] = {
|
98
97
|
# Removed .gitignore from exceptions - it should be hidden by default
|
99
98
|
".env.example",
|
100
99
|
".env.sample",
|
@@ -260,7 +259,9 @@ class GitignoreManager:
|
|
260
259
|
|
261
260
|
patterns = []
|
262
261
|
try:
|
263
|
-
with
|
262
|
+
with Path(gitignore_path).open(
|
263
|
+
encoding="utf-8",
|
264
|
+
) as f:
|
264
265
|
for line in f:
|
265
266
|
line = line.strip()
|
266
267
|
# Skip empty lines and comments
|
@@ -383,7 +384,9 @@ class PythonAnalyzer:
|
|
383
384
|
nodes = []
|
384
385
|
|
385
386
|
try:
|
386
|
-
with
|
387
|
+
with Path(file_path).open(
|
388
|
+
encoding="utf-8",
|
389
|
+
) as f:
|
387
390
|
source = f.read()
|
388
391
|
|
389
392
|
tree = ast.parse(source, filename=str(file_path))
|
@@ -635,7 +638,7 @@ class MultiLanguageAnalyzer:
|
|
635
638
|
allowing us to support JavaScript, TypeScript, and other languages.
|
636
639
|
"""
|
637
640
|
|
638
|
-
LANGUAGE_PARSERS = {
|
641
|
+
LANGUAGE_PARSERS: ClassVar[dict] = {
|
639
642
|
"python": "tree_sitter_python",
|
640
643
|
"javascript": "tree_sitter_javascript",
|
641
644
|
"typescript": "tree_sitter_typescript",
|
@@ -837,7 +840,7 @@ class CodeTreeAnalyzer:
|
|
837
840
|
"""
|
838
841
|
|
839
842
|
# Define code file extensions at class level for directory filtering
|
840
|
-
CODE_EXTENSIONS = {
|
843
|
+
CODE_EXTENSIONS: ClassVar[set] = {
|
841
844
|
".py",
|
842
845
|
".js",
|
843
846
|
".jsx",
|
@@ -888,7 +891,7 @@ class CodeTreeAnalyzer:
|
|
888
891
|
}
|
889
892
|
|
890
893
|
# File extensions to language mapping
|
891
|
-
LANGUAGE_MAP = {
|
894
|
+
LANGUAGE_MAP: ClassVar[dict] = {
|
892
895
|
".py": "python",
|
893
896
|
".js": "javascript",
|
894
897
|
".jsx": "javascript",
|
@@ -8,6 +8,9 @@ for developers working on the claude-mpm dashboard.
|
|
8
8
|
|
9
9
|
import argparse
|
10
10
|
import asyncio
|
11
|
+
|
12
|
+
# Try to import Rich for enhanced output
|
13
|
+
import importlib.util
|
11
14
|
import json
|
12
15
|
import signal
|
13
16
|
import sys
|
@@ -20,18 +23,15 @@ from typing import Any, Dict, List, Optional, Set
|
|
20
23
|
|
21
24
|
import socketio
|
22
25
|
|
23
|
-
|
24
|
-
try:
|
26
|
+
if importlib.util.find_spec("rich"):
|
25
27
|
from rich.console import Console
|
26
|
-
from rich.layout import Layout
|
27
|
-
from rich.live import Live
|
28
28
|
from rich.panel import Panel
|
29
29
|
from rich.table import Table
|
30
30
|
from rich.text import Text
|
31
31
|
|
32
32
|
RICH_AVAILABLE = True
|
33
33
|
console = Console()
|
34
|
-
|
34
|
+
else:
|
35
35
|
RICH_AVAILABLE = False
|
36
36
|
console = None
|
37
37
|
|
@@ -266,7 +266,7 @@ class SocketIODebugger:
|
|
266
266
|
"PostToolUse": "✅",
|
267
267
|
"Error": "❌",
|
268
268
|
"Warning": "⚠️",
|
269
|
-
"Info": "
|
269
|
+
"Info": "[INFO]️",
|
270
270
|
"MemoryUpdate": "🧠",
|
271
271
|
"ConfigChange": "⚙️",
|
272
272
|
}
|
@@ -463,7 +463,7 @@ class SocketIODebugger:
|
|
463
463
|
console.print(f"[{timestamp}] {message}", style=style)
|
464
464
|
else:
|
465
465
|
prefixes = {
|
466
|
-
"info": "
|
466
|
+
"info": "[INFO]️",
|
467
467
|
"success": "✅",
|
468
468
|
"warning": "⚠️",
|
469
469
|
"error": "❌",
|
claude_mpm/utils/common.py
CHANGED
@@ -46,7 +46,9 @@ def load_json_safe(
|
|
46
46
|
file_path = Path(file_path)
|
47
47
|
|
48
48
|
try:
|
49
|
-
with
|
49
|
+
with Path(file_path).open(
|
50
|
+
encoding=encoding,
|
51
|
+
) as f:
|
50
52
|
return json.load(f)
|
51
53
|
except FileNotFoundError:
|
52
54
|
logger.debug(f"JSON file not found: {file_path}")
|
@@ -112,7 +114,9 @@ def load_yaml_safe(
|
|
112
114
|
file_path = Path(file_path)
|
113
115
|
|
114
116
|
try:
|
115
|
-
with
|
117
|
+
with Path(file_path).open(
|
118
|
+
encoding=encoding,
|
119
|
+
) as f:
|
116
120
|
return yaml.safe_load(f) or default or {}
|
117
121
|
except FileNotFoundError:
|
118
122
|
logger.debug(f"YAML file not found: {file_path}")
|
@@ -99,7 +99,9 @@ class ConfigurationManager:
|
|
99
99
|
|
100
100
|
logger.debug(f"Loading JSON configuration from {file_path}")
|
101
101
|
try:
|
102
|
-
with
|
102
|
+
with Path(file_path).open(
|
103
|
+
encoding="utf-8",
|
104
|
+
) as f:
|
103
105
|
config = json.load(f)
|
104
106
|
self._update_cache(file_path, config)
|
105
107
|
return config
|
@@ -142,7 +144,9 @@ class ConfigurationManager:
|
|
142
144
|
|
143
145
|
logger.debug(f"Loading YAML configuration from {file_path}")
|
144
146
|
try:
|
145
|
-
with
|
147
|
+
with Path(file_path).open(
|
148
|
+
encoding="utf-8",
|
149
|
+
) as f:
|
146
150
|
config = yaml.safe_load(f) or {}
|
147
151
|
self._update_cache(file_path, config)
|
148
152
|
return config
|
@@ -185,7 +189,9 @@ class ConfigurationManager:
|
|
185
189
|
|
186
190
|
logger.debug(f"Loading TOML configuration from {file_path}")
|
187
191
|
try:
|
188
|
-
with
|
192
|
+
with Path(file_path).open(
|
193
|
+
encoding="utf-8",
|
194
|
+
) as f:
|
189
195
|
config = toml.load(f)
|
190
196
|
self._update_cache(file_path, config)
|
191
197
|
return config
|
@@ -10,7 +10,7 @@ DESIGN DECISION: We prioritize pure Python implementations over native ones
|
|
10
10
|
for better cross-platform compatibility, even if they might be slightly slower.
|
11
11
|
"""
|
12
12
|
|
13
|
-
from typing import Any, Dict, Optional, Tuple
|
13
|
+
from typing import Any, ClassVar, Dict, Optional, Tuple
|
14
14
|
|
15
15
|
from ..core.logger import get_logger
|
16
16
|
|
@@ -26,19 +26,19 @@ class DatabaseConnector:
|
|
26
26
|
"""
|
27
27
|
|
28
28
|
# Database drivers in order of preference (first is preferred)
|
29
|
-
MYSQL_DRIVERS = [
|
29
|
+
MYSQL_DRIVERS: ClassVar[list] = [
|
30
30
|
("pymysql", "pymysql"), # Pure Python, no compilation required
|
31
31
|
("mysqlclient", "MySQLdb"), # Faster but requires MySQL dev headers
|
32
32
|
("mysql-connector-python", "mysql.connector"), # Oracle's pure Python driver
|
33
33
|
]
|
34
34
|
|
35
|
-
POSTGRESQL_DRIVERS = [
|
35
|
+
POSTGRESQL_DRIVERS: ClassVar[list] = [
|
36
36
|
("psycopg2-binary", "psycopg2"), # Binary wheel, no compilation
|
37
37
|
("psycopg2", "psycopg2"), # Requires PostgreSQL dev headers
|
38
38
|
("pg8000", "pg8000"), # Pure Python alternative
|
39
39
|
]
|
40
40
|
|
41
|
-
ORACLE_DRIVERS = [
|
41
|
+
ORACLE_DRIVERS: ClassVar[list] = [
|
42
42
|
("cx_Oracle", "cx_Oracle"), # Requires Oracle client libraries
|
43
43
|
("oracledb", "oracledb"), # Pure Python Oracle driver (newer)
|
44
44
|
]
|
@@ -106,7 +106,7 @@ class DependencyStrategy:
|
|
106
106
|
def _is_docker(self) -> bool:
|
107
107
|
"""Check if running inside Docker container."""
|
108
108
|
return (
|
109
|
-
|
109
|
+
Path("/.dockerenv").exists()
|
110
110
|
or os.environ.get("KUBERNETES_SERVICE_HOST") is not None
|
111
111
|
)
|
112
112
|
|
@@ -7,6 +7,7 @@ prompting is appropriate for dependency installation.
|
|
7
7
|
|
8
8
|
import os
|
9
9
|
import sys
|
10
|
+
from pathlib import Path
|
10
11
|
from typing import Dict, Tuple
|
11
12
|
|
12
13
|
from ..core.logger import get_logger
|
@@ -139,7 +140,7 @@ class EnvironmentContext:
|
|
139
140
|
"""
|
140
141
|
# Check for Docker-specific files
|
141
142
|
for indicator_file in cls.CONTAINER_INDICATORS:
|
142
|
-
if
|
143
|
+
if Path(indicator_file).exists():
|
143
144
|
logger.debug(f"Container detected via {indicator_file}")
|
144
145
|
return True
|
145
146
|
|
@@ -149,7 +150,7 @@ class EnvironmentContext:
|
|
149
150
|
|
150
151
|
# Check cgroup for docker/containerd references
|
151
152
|
try:
|
152
|
-
with
|
153
|
+
with Path("/proc/1/cgroup").open() as f:
|
153
154
|
cgroup_content = f.read()
|
154
155
|
if "docker" in cgroup_content or "containerd" in cgroup_content:
|
155
156
|
return True
|
claude_mpm/utils/file_utils.py
CHANGED
@@ -7,7 +7,6 @@ error handling, and directory management.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
import json
|
10
|
-
import os
|
11
10
|
import shutil
|
12
11
|
import tempfile
|
13
12
|
from pathlib import Path
|
@@ -149,7 +148,7 @@ def atomic_write(
|
|
149
148
|
# Clean up temporary file if it exists
|
150
149
|
try:
|
151
150
|
if "temp_path" in locals():
|
152
|
-
|
151
|
+
Path(temp_path).unlink()
|
153
152
|
except Exception:
|
154
153
|
pass
|
155
154
|
raise FileOperationError(f"Failed to atomically write file {path}: {e}") from e
|
@@ -136,7 +136,9 @@ class PathOperations:
|
|
136
136
|
logger.warning(f"File not readable: {path}")
|
137
137
|
return default
|
138
138
|
|
139
|
-
with
|
139
|
+
with Path(path_obj).open(
|
140
|
+
encoding=encoding,
|
141
|
+
) as f:
|
140
142
|
return f.read()
|
141
143
|
except Exception as e:
|
142
144
|
logger.error(f"Error reading file {path}: {e}")
|
@@ -100,10 +100,9 @@ class RobustPackageInstaller:
|
|
100
100
|
Tuple of (success, error_message)
|
101
101
|
"""
|
102
102
|
# Check success cache first
|
103
|
-
if
|
104
|
-
|
105
|
-
|
106
|
-
return True, None
|
103
|
+
if self.success_cache.get(package_spec):
|
104
|
+
logger.debug(f"Package {package_spec} already successfully installed")
|
105
|
+
return True, None
|
107
106
|
|
108
107
|
# Default strategy order
|
109
108
|
if strategies is None:
|
@@ -8,7 +8,7 @@ Critical for ensuring agents work correctly with Claude Code.
|
|
8
8
|
"""
|
9
9
|
|
10
10
|
import re
|
11
|
-
from typing import Dict, List, Optional, Tuple
|
11
|
+
from typing import ClassVar, Dict, List, Optional, Tuple
|
12
12
|
|
13
13
|
import yaml
|
14
14
|
|
@@ -21,7 +21,7 @@ class FrontmatterValidator:
|
|
21
21
|
NAME_PATTERN = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
|
22
22
|
|
23
23
|
# Valid tool names (from Claude Code spec)
|
24
|
-
VALID_TOOLS = {
|
24
|
+
VALID_TOOLS: ClassVar[set] = {
|
25
25
|
"Read",
|
26
26
|
"Write",
|
27
27
|
"Edit",
|
@@ -41,10 +41,10 @@ class FrontmatterValidator:
|
|
41
41
|
}
|
42
42
|
|
43
43
|
# Valid model tiers
|
44
|
-
VALID_MODELS = {"opus", "sonnet", "haiku"}
|
44
|
+
VALID_MODELS: ClassVar[set] = {"opus", "sonnet", "haiku"}
|
45
45
|
|
46
46
|
# Required fields in frontmatter
|
47
|
-
REQUIRED_FIELDS = {"name", "description", "tools"}
|
47
|
+
REQUIRED_FIELDS: ClassVar[set] = {"name", "description", "tools"}
|
48
48
|
|
49
49
|
@classmethod
|
50
50
|
def validate_name(cls, name: str) -> Tuple[bool, Optional[str]]:
|