claude-mpm 4.5.11__py3-none-any.whl → 4.5.13__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 +47 -0
- claude_mpm/agents/BASE_QA.md +60 -0
- claude_mpm/agents/frontmatter_validator.py +4 -4
- claude_mpm/agents/templates/nextjs_engineer.json +2 -2
- claude_mpm/agents/templates/qa.json +13 -3
- claude_mpm/agents/templates/react_engineer.json +2 -2
- claude_mpm/agents/templates/typescript_engineer.json +2 -2
- claude_mpm/agents/templates/web_qa.json +14 -3
- claude_mpm/cli/commands/agent_manager.py +3 -3
- claude_mpm/cli/commands/agents.py +6 -6
- claude_mpm/cli/commands/aggregate.py +4 -4
- claude_mpm/cli/commands/analyze.py +2 -2
- claude_mpm/cli/commands/analyze_code.py +1 -1
- claude_mpm/cli/commands/cleanup.py +3 -3
- claude_mpm/cli/commands/config.py +2 -2
- claude_mpm/cli/commands/configure.py +14 -14
- claude_mpm/cli/commands/dashboard.py +1 -1
- claude_mpm/cli/commands/debug.py +3 -3
- claude_mpm/cli/commands/doctor.py +1 -1
- claude_mpm/cli/commands/mcp.py +7 -7
- claude_mpm/cli/commands/mcp_command_router.py +1 -1
- claude_mpm/cli/commands/mcp_config.py +2 -2
- claude_mpm/cli/commands/mcp_external_commands.py +2 -2
- claude_mpm/cli/commands/mcp_install_commands.py +3 -3
- claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
- claude_mpm/cli/commands/mcp_setup_external.py +3 -3
- claude_mpm/cli/commands/monitor.py +1 -1
- claude_mpm/cli/commands/mpm_init_handler.py +1 -1
- claude_mpm/cli/interactive/agent_wizard.py +1 -1
- claude_mpm/cli/parsers/search_parser.py +1 -1
- claude_mpm/cli/shared/argument_patterns.py +2 -2
- claude_mpm/cli/shared/base_command.py +1 -1
- claude_mpm/cli/startup_logging.py +6 -4
- claude_mpm/config/experimental_features.py +4 -4
- claude_mpm/config/socketio_config.py +2 -2
- claude_mpm/core/agent_session_manager.py +2 -2
- claude_mpm/core/api_validator.py +3 -3
- claude_mpm/core/base_service.py +10 -1
- claude_mpm/core/cache.py +2 -2
- claude_mpm/core/config.py +4 -4
- claude_mpm/core/config_aliases.py +4 -4
- claude_mpm/core/config_constants.py +1 -1
- claude_mpm/core/error_handler.py +1 -1
- claude_mpm/core/file_utils.py +5 -5
- claude_mpm/core/framework/formatters/capability_generator.py +5 -5
- claude_mpm/core/framework/loaders/agent_loader.py +1 -1
- claude_mpm/core/framework/processors/metadata_processor.py +1 -1
- claude_mpm/core/framework/processors/template_processor.py +3 -3
- claude_mpm/core/framework_loader.py +2 -2
- claude_mpm/core/log_manager.py +4 -4
- claude_mpm/core/logger.py +2 -2
- claude_mpm/core/optimized_startup.py +1 -1
- claude_mpm/core/output_style_manager.py +1 -1
- claude_mpm/core/service_registry.py +2 -2
- claude_mpm/core/session_manager.py +3 -3
- claude_mpm/core/shared/config_loader.py +1 -1
- claude_mpm/core/socketio_pool.py +2 -2
- claude_mpm/core/unified_agent_registry.py +2 -2
- claude_mpm/core/unified_config.py +6 -6
- claude_mpm/core/unified_paths.py +2 -2
- claude_mpm/dashboard/api/simple_directory.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +1 -1
- claude_mpm/hooks/claude_hooks/event_handlers.py +2 -2
- claude_mpm/hooks/claude_hooks/installer.py +9 -9
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +7 -2
- claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
- claude_mpm/hooks/memory_integration_hook.py +1 -1
- claude_mpm/hooks/validation_hooks.py +1 -1
- claude_mpm/init.py +4 -4
- claude_mpm/models/agent_session.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +5 -5
- claude_mpm/services/__init__.py +2 -2
- claude_mpm/services/agent_capabilities_service.py +1 -1
- claude_mpm/services/agents/agent_builder.py +6 -4
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +3 -3
- claude_mpm/services/agents/deployment/deployment_wrapper.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +2 -2
- claude_mpm/services/agents/local_template_manager.py +5 -5
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
- claude_mpm/services/agents/registry/modification_tracker.py +19 -11
- claude_mpm/services/async_session_logger.py +1 -1
- claude_mpm/services/claude_session_logger.py +1 -1
- claude_mpm/services/cli/agent_listing_service.py +3 -3
- claude_mpm/services/cli/agent_validation_service.py +1 -1
- claude_mpm/services/cli/session_manager.py +2 -2
- claude_mpm/services/core/path_resolver.py +1 -1
- claude_mpm/services/diagnostics/checks/agent_check.py +1 -1
- claude_mpm/services/diagnostics/checks/claude_code_check.py +2 -2
- claude_mpm/services/diagnostics/checks/common_issues_check.py +3 -3
- claude_mpm/services/diagnostics/checks/configuration_check.py +2 -2
- claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
- claude_mpm/services/diagnostics/checks/mcp_check.py +1 -1
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +9 -9
- claude_mpm/services/diagnostics/checks/monitor_check.py +1 -1
- claude_mpm/services/diagnostics/doctor_reporter.py +1 -1
- claude_mpm/services/event_aggregator.py +1 -1
- claude_mpm/services/event_bus/event_bus.py +9 -2
- claude_mpm/services/events/consumers/dead_letter.py +2 -2
- claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
- claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
- claude_mpm/services/hook_installer_service.py +7 -7
- claude_mpm/services/infrastructure/context_preservation.py +7 -7
- claude_mpm/services/infrastructure/daemon_manager.py +5 -5
- claude_mpm/services/mcp_config_manager.py +10 -10
- claude_mpm/services/mcp_gateway/auto_configure.py +5 -5
- claude_mpm/services/mcp_gateway/config/config_loader.py +2 -2
- claude_mpm/services/mcp_gateway/config/configuration.py +5 -3
- claude_mpm/services/mcp_gateway/core/process_pool.py +3 -3
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
- claude_mpm/services/mcp_gateway/core/startup_verification.py +1 -1
- claude_mpm/services/mcp_gateway/main.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +2 -1
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +1 -1
- claude_mpm/services/mcp_gateway/tools/hello_world.py +1 -1
- claude_mpm/services/mcp_gateway/utils/package_version_checker.py +5 -5
- claude_mpm/services/mcp_gateway/utils/update_preferences.py +2 -2
- claude_mpm/services/mcp_service_verifier.py +1 -1
- claude_mpm/services/memory/builder.py +1 -1
- claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
- claude_mpm/services/memory/indexed_memory.py +3 -3
- claude_mpm/services/monitor/daemon.py +1 -1
- claude_mpm/services/monitor/daemon_manager.py +9 -9
- claude_mpm/services/monitor/handlers/file.py +1 -1
- claude_mpm/services/monitor/handlers/hooks.py +3 -3
- claude_mpm/services/monitor/management/lifecycle.py +7 -7
- claude_mpm/services/monitor/server.py +2 -2
- claude_mpm/services/orphan_detection.py +11 -16
- claude_mpm/services/port_manager.py +2 -2
- claude_mpm/services/project/analyzer.py +3 -3
- claude_mpm/services/project/archive_manager.py +17 -13
- claude_mpm/services/project/dependency_analyzer.py +4 -4
- claude_mpm/services/project/documentation_manager.py +4 -4
- claude_mpm/services/project/enhanced_analyzer.py +19 -8
- claude_mpm/services/project/registry.py +4 -4
- claude_mpm/services/project_port_allocator.py +7 -12
- claude_mpm/services/session_management_service.py +1 -1
- claude_mpm/services/socketio/event_normalizer.py +1 -1
- claude_mpm/services/socketio/handlers/code_analysis.py +14 -12
- claude_mpm/services/socketio/handlers/file.py +1 -1
- claude_mpm/services/socketio/migration_utils.py +1 -1
- claude_mpm/services/socketio/server/core.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +4 -4
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +1 -1
- claude_mpm/services/unified/config_strategies/config_schema.py +4 -4
- claude_mpm/services/unified/config_strategies/context_strategy.py +8 -6
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +10 -10
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +5 -5
- claude_mpm/services/unified/config_strategies/unified_config_service.py +8 -8
- claude_mpm/services/unified/config_strategies/validation_strategy.py +15 -15
- claude_mpm/services/unified/deployment_strategies/base.py +4 -4
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +15 -15
- claude_mpm/services/unified/deployment_strategies/local.py +11 -11
- claude_mpm/services/unified/deployment_strategies/utils.py +11 -9
- claude_mpm/services/unified/deployment_strategies/vercel.py +7 -9
- claude_mpm/services/unified/unified_config.py +5 -5
- claude_mpm/services/unified/unified_deployment.py +2 -2
- claude_mpm/services/utility_service.py +1 -1
- claude_mpm/services/version_control/conflict_resolution.py +2 -2
- claude_mpm/services/version_control/git_operations.py +3 -3
- claude_mpm/services/version_control/semantic_versioning.py +13 -13
- claude_mpm/services/version_control/version_parser.py +1 -1
- claude_mpm/storage/state_storage.py +12 -13
- claude_mpm/tools/code_tree_analyzer.py +5 -5
- claude_mpm/tools/code_tree_builder.py +4 -4
- claude_mpm/tools/socketio_debug.py +1 -1
- claude_mpm/utils/agent_dependency_loader.py +4 -4
- claude_mpm/utils/common.py +2 -2
- claude_mpm/utils/config_manager.py +3 -3
- claude_mpm/utils/dependency_cache.py +2 -2
- claude_mpm/utils/dependency_strategies.py +6 -6
- claude_mpm/utils/file_utils.py +11 -11
- claude_mpm/utils/log_cleanup.py +1 -1
- claude_mpm/utils/path_operations.py +1 -1
- claude_mpm/validation/agent_validator.py +2 -2
- claude_mpm/validation/frontmatter_validator.py +1 -1
- {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/METADATA +1 -1
- {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/RECORD +190 -190
- {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/WHEEL +0 -0
- {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/top_level.txt +0 -0
claude_mpm/core/file_utils.py
CHANGED
@@ -80,8 +80,8 @@ def safe_path_join(*parts: Union[str, Path]) -> Path:
|
|
80
80
|
# Ensure the resolved path is under the base path
|
81
81
|
try:
|
82
82
|
resolved.relative_to(base)
|
83
|
-
except ValueError:
|
84
|
-
raise ValueError(f"Path traversal detected: {path}")
|
83
|
+
except ValueError as e:
|
84
|
+
raise ValueError(f"Path traversal detected: {path}") from e
|
85
85
|
|
86
86
|
return resolved
|
87
87
|
|
@@ -142,7 +142,7 @@ def safe_read(
|
|
142
142
|
"""Safely read a file with error handling.
|
143
143
|
|
144
144
|
Replaces the common pattern:
|
145
|
-
with open(
|
145
|
+
with file.open('r') as f:
|
146
146
|
content = f.read()
|
147
147
|
|
148
148
|
Args:
|
@@ -641,7 +641,7 @@ def file_lock(filepath: Union[str, Path], timeout: float = 5.0):
|
|
641
641
|
if e.errno != errno.EAGAIN:
|
642
642
|
raise
|
643
643
|
if time.time() - start_time > timeout:
|
644
|
-
raise TimeoutError(f"Could not acquire lock for {filepath}")
|
644
|
+
raise TimeoutError(f"Could not acquire lock for {filepath}") from e
|
645
645
|
time.sleep(0.1)
|
646
646
|
|
647
647
|
yield lock_handle
|
@@ -724,7 +724,7 @@ def get_file_hash(
|
|
724
724
|
|
725
725
|
try:
|
726
726
|
hasher = hashlib.new(algorithm)
|
727
|
-
with open(
|
727
|
+
with filepath.open("rb") as f:
|
728
728
|
for chunk in iter(lambda: f.read(8192), b""):
|
729
729
|
hasher.update(chunk)
|
730
730
|
return hasher.hexdigest()
|
@@ -154,7 +154,7 @@ class CapabilityGenerator:
|
|
154
154
|
Dictionary with agent metadata or None
|
155
155
|
"""
|
156
156
|
try:
|
157
|
-
with open(
|
157
|
+
with agent_file.open() as f:
|
158
158
|
content = f.read()
|
159
159
|
|
160
160
|
# Default values
|
@@ -244,7 +244,7 @@ class CapabilityGenerator:
|
|
244
244
|
template_file = templates_dir / f"{agent_name}.json"
|
245
245
|
|
246
246
|
if template_file.exists():
|
247
|
-
with open(
|
247
|
+
with template_file.open() as f:
|
248
248
|
template_data = json.load(f)
|
249
249
|
return template_data.get("routing")
|
250
250
|
|
@@ -262,7 +262,7 @@ class CapabilityGenerator:
|
|
262
262
|
if alt_name != agent_name:
|
263
263
|
alt_file = templates_dir / f"{alt_name}.json"
|
264
264
|
if alt_file.exists():
|
265
|
-
with open(
|
265
|
+
with alt_file.open() as f:
|
266
266
|
template_data = json.load(f)
|
267
267
|
return template_data.get("routing")
|
268
268
|
|
@@ -310,7 +310,7 @@ class CapabilityGenerator:
|
|
310
310
|
template_file = templates_dir / f"{agent_name}.json"
|
311
311
|
|
312
312
|
if template_file.exists():
|
313
|
-
with open(
|
313
|
+
with template_file.open() as f:
|
314
314
|
template_data = json.load(f)
|
315
315
|
return template_data.get("memory_routing")
|
316
316
|
|
@@ -330,7 +330,7 @@ class CapabilityGenerator:
|
|
330
330
|
if alt_name != agent_name:
|
331
331
|
alt_file = templates_dir / f"{alt_name}.json"
|
332
332
|
if alt_file.exists():
|
333
|
-
with open(
|
333
|
+
with alt_file.open() as f:
|
334
334
|
template_data = json.load(f)
|
335
335
|
return template_data.get("memory_routing")
|
336
336
|
|
@@ -89,7 +89,7 @@ class TemplateProcessor:
|
|
89
89
|
# Try exact match first
|
90
90
|
template_file = templates_dir / f"{agent_name}.json"
|
91
91
|
if template_file.exists():
|
92
|
-
with open(
|
92
|
+
with template_file.open() as f:
|
93
93
|
return json.load(f)
|
94
94
|
|
95
95
|
# Try alternative naming variations
|
@@ -97,7 +97,7 @@ class TemplateProcessor:
|
|
97
97
|
for alt_name in alternative_names:
|
98
98
|
alt_file = templates_dir / f"{alt_name}.json"
|
99
99
|
if alt_file.exists():
|
100
|
-
with open(
|
100
|
+
with alt_file.open() as f:
|
101
101
|
return json.load(f)
|
102
102
|
|
103
103
|
return None
|
@@ -218,7 +218,7 @@ class TemplateProcessor:
|
|
218
218
|
|
219
219
|
for json_file in template_dir.glob("*.json"):
|
220
220
|
try:
|
221
|
-
with open(
|
221
|
+
with json_file.open() as f:
|
222
222
|
template_data = json.load(f)
|
223
223
|
|
224
224
|
agent_metadata = self.extract_metadata(template_data)
|
@@ -504,9 +504,9 @@ class FrameworkLoader:
|
|
504
504
|
metadata["instructions_length"] = len(instructions)
|
505
505
|
|
506
506
|
if loop.is_running():
|
507
|
-
asyncio.create_task(
|
507
|
+
_task = asyncio.create_task( # noqa: RUF006
|
508
508
|
log_manager.log_prompt("system_prompt", instructions, metadata)
|
509
|
-
)
|
509
|
+
) # Fire-and-forget logging
|
510
510
|
else:
|
511
511
|
loop.run_until_complete(
|
512
512
|
log_manager.log_prompt("system_prompt", instructions, metadata)
|
claude_mpm/core/log_manager.py
CHANGED
@@ -542,12 +542,12 @@ class LogManager:
|
|
542
542
|
|
543
543
|
if extension == ".json":
|
544
544
|
# JSON files also get structured metadata for consistency
|
545
|
-
with open(
|
545
|
+
with file_path.open("w", encoding="utf-8") as f:
|
546
546
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
547
547
|
# For markdown or text files
|
548
548
|
elif isinstance(data, dict):
|
549
549
|
# Write as formatted markdown with metadata
|
550
|
-
with open(
|
550
|
+
with file_path.open("w", encoding="utf-8") as f:
|
551
551
|
f.write("---\n")
|
552
552
|
f.write(f"timestamp: {data.get('timestamp', 'unknown')}\n")
|
553
553
|
f.write(f"type: {data.get('type', 'unknown')}\n")
|
@@ -559,7 +559,7 @@ class LogManager:
|
|
559
559
|
f.write(data.get("content", ""))
|
560
560
|
else:
|
561
561
|
# Write content directly
|
562
|
-
with open(
|
562
|
+
with file_path.open("w", encoding="utf-8") as f:
|
563
563
|
f.write(str(data))
|
564
564
|
except Exception as e:
|
565
565
|
logger.error(f"Failed to write {file_path}: {e}")
|
@@ -588,7 +588,7 @@ class LogManager:
|
|
588
588
|
|
589
589
|
def write_task():
|
590
590
|
try:
|
591
|
-
with open(
|
591
|
+
with log_file.open("a", encoding="utf-8") as f:
|
592
592
|
f.write(log_entry)
|
593
593
|
except Exception as e:
|
594
594
|
logger.error(f"Failed to write log: {e}")
|
claude_mpm/core/logger.py
CHANGED
@@ -526,7 +526,7 @@ class ProjectLogger:
|
|
526
526
|
self.dirs["logs_system"]
|
527
527
|
/ f"{datetime.now(timezone.utc).strftime('%Y%m%d')}.jsonl"
|
528
528
|
)
|
529
|
-
with open(
|
529
|
+
with log_file.open("a") as f:
|
530
530
|
f.write(json.dumps(log_entry) + "\n")
|
531
531
|
|
532
532
|
def log_agent_invocation(
|
@@ -580,7 +580,7 @@ class ProjectLogger:
|
|
580
580
|
daily_log = (
|
581
581
|
agent_log_dir / f"{datetime.now(timezone.utc).strftime('%Y%m%d')}.jsonl"
|
582
582
|
)
|
583
|
-
with open(
|
583
|
+
with daily_log.open("a") as f:
|
584
584
|
f.write(json.dumps(log_entry) + "\n")
|
585
585
|
|
586
586
|
def get_session_summary(self) -> Dict[str, Any]:
|
@@ -42,7 +42,7 @@ class OutputStyleManager:
|
|
42
42
|
Path(__file__).parent.parent / "agents" / "OUTPUT_STYLE.md"
|
43
43
|
)
|
44
44
|
|
45
|
-
def _detect_claude_version(self) -> Optional[str]:
|
45
|
+
def _detect_claude_version(self) -> Optional[str]:
|
46
46
|
"""
|
47
47
|
Detect Claude Code version by running 'claude --version'.
|
48
48
|
Uses global cache to avoid duplicate detection and logging.
|
@@ -160,10 +160,10 @@ class ServiceRegistry:
|
|
160
160
|
try:
|
161
161
|
# Use the enhanced container's named resolution
|
162
162
|
return self.container.get(BaseService, name=service_type)
|
163
|
-
except Exception:
|
163
|
+
except Exception as e:
|
164
164
|
# Fall back to looking up class and resolving
|
165
165
|
if service_type not in self._services:
|
166
|
-
raise KeyError(f"Service '{service_type}' not registered")
|
166
|
+
raise KeyError(f"Service '{service_type}' not registered") from e
|
167
167
|
service_class = self._services[service_type]
|
168
168
|
return self.container.get(service_class)
|
169
169
|
else:
|
@@ -187,7 +187,7 @@ class SessionManager:
|
|
187
187
|
"""Save sessions to disk."""
|
188
188
|
session_file = self.session_dir / "active_sessions.json"
|
189
189
|
try:
|
190
|
-
with open(
|
190
|
+
with session_file.open("w") as f:
|
191
191
|
json.dump(self.active_sessions, f, indent=2)
|
192
192
|
except Exception as e:
|
193
193
|
logger.error(f"Failed to save sessions: {e}")
|
@@ -197,7 +197,7 @@ class SessionManager:
|
|
197
197
|
session_file = self.session_dir / "active_sessions.json"
|
198
198
|
if session_file.exists():
|
199
199
|
try:
|
200
|
-
with open(
|
200
|
+
with session_file.open() as f:
|
201
201
|
self.active_sessions = json.load(f)
|
202
202
|
|
203
203
|
# Clean up old sessions on load (archive by default)
|
@@ -286,7 +286,7 @@ class SessionManager:
|
|
286
286
|
backup_path = archive_dir / backup_name
|
287
287
|
|
288
288
|
# Compress and backup current file
|
289
|
-
with open(
|
289
|
+
with claude_json_path.open("rb") as f_in:
|
290
290
|
with gzip.open(backup_path, "wb") as f_out:
|
291
291
|
shutil.copyfileobj(f_in, f_out)
|
292
292
|
|
claude_mpm/core/socketio_pool.py
CHANGED
@@ -594,9 +594,9 @@ class SocketIOConnectionPool:
|
|
594
594
|
timeout=2.0,
|
595
595
|
)
|
596
596
|
|
597
|
-
except asyncio.TimeoutError:
|
597
|
+
except asyncio.TimeoutError as e:
|
598
598
|
self.logger.debug("Socket.IO connection timeout")
|
599
|
-
raise TimeoutError("Socket.IO connection timeout")
|
599
|
+
raise TimeoutError("Socket.IO connection timeout") from e
|
600
600
|
except Exception as e:
|
601
601
|
self.logger.debug(f"Client connection failed: {e}")
|
602
602
|
raise
|
@@ -728,7 +728,7 @@ class UnifiedAgentRegistry:
|
|
728
728
|
},
|
729
729
|
}
|
730
730
|
|
731
|
-
with open(
|
731
|
+
with output_path.open("w") as f:
|
732
732
|
json.dump(export_data, f, indent=2)
|
733
733
|
|
734
734
|
logger.info(f"Exported {len(self.registry)} agents to {output_path}")
|
@@ -737,7 +737,7 @@ class UnifiedAgentRegistry:
|
|
737
737
|
"""Import registry from JSON file."""
|
738
738
|
input_path = Path(input_path)
|
739
739
|
|
740
|
-
with open(
|
740
|
+
with input_path.open() as f:
|
741
741
|
data = json.load(f)
|
742
742
|
|
743
743
|
# Clear current registry
|
@@ -436,7 +436,7 @@ class ConfigurationService:
|
|
436
436
|
if config_path.exists():
|
437
437
|
import yaml
|
438
438
|
|
439
|
-
with open(
|
439
|
+
with config_path.open() as f:
|
440
440
|
file_config = yaml.safe_load(f) or {}
|
441
441
|
config_data.update(file_config)
|
442
442
|
break
|
@@ -448,7 +448,7 @@ class ConfigurationService:
|
|
448
448
|
raise ConfigurationError(
|
449
449
|
f"Failed to load configuration: {e}",
|
450
450
|
context={"error_type": type(e).__name__},
|
451
|
-
)
|
451
|
+
) from e
|
452
452
|
|
453
453
|
@property
|
454
454
|
def config(self) -> UnifiedConfig:
|
@@ -515,7 +515,7 @@ class ConfigurationService:
|
|
515
515
|
return True
|
516
516
|
raise ConfigurationError("Invalid SocketIO port range")
|
517
517
|
except Exception as e:
|
518
|
-
raise ConfigurationError(f"Configuration validation failed: {e}")
|
518
|
+
raise ConfigurationError(f"Configuration validation failed: {e}") from e
|
519
519
|
|
520
520
|
def export_to_file(self, file_path: Union[str, Path], format: str = "yaml") -> None:
|
521
521
|
"""
|
@@ -531,12 +531,12 @@ class ConfigurationService:
|
|
531
531
|
if format.lower() == "yaml":
|
532
532
|
import yaml
|
533
533
|
|
534
|
-
with open(
|
534
|
+
with file_path.open("w") as f:
|
535
535
|
yaml.dump(self._config.dict(), f, default_flow_style=False)
|
536
536
|
elif format.lower() == "json":
|
537
537
|
import json
|
538
538
|
|
539
|
-
with open(
|
539
|
+
with file_path.open("w") as f:
|
540
540
|
json.dump(self._config.dict(), f, indent=2)
|
541
541
|
else:
|
542
542
|
raise ConfigurationError(f"Unsupported export format: {format}")
|
@@ -545,4 +545,4 @@ class ConfigurationService:
|
|
545
545
|
raise ConfigurationError(
|
546
546
|
f"Failed to export configuration: {e}",
|
547
547
|
context={"file_path": str(file_path), "format": format},
|
548
|
-
)
|
548
|
+
) from e
|
claude_mpm/core/unified_paths.py
CHANGED
@@ -167,7 +167,7 @@ class PathContext:
|
|
167
167
|
|
168
168
|
@staticmethod
|
169
169
|
@lru_cache(maxsize=1)
|
170
|
-
def detect_deployment_context() -> DeploymentContext:
|
170
|
+
def detect_deployment_context() -> DeploymentContext:
|
171
171
|
"""Detect the current deployment context.
|
172
172
|
|
173
173
|
Priority order:
|
@@ -396,7 +396,7 @@ class UnifiedPathManager:
|
|
396
396
|
return current
|
397
397
|
current = current.parent
|
398
398
|
|
399
|
-
raise FileNotFoundError("Could not determine framework root")
|
399
|
+
raise FileNotFoundError("Could not determine framework root") from None
|
400
400
|
|
401
401
|
@property
|
402
402
|
@lru_cache(maxsize=1)
|
@@ -129,7 +129,7 @@ def has_code_files(directory_path, max_depth=5, current_depth=0):
|
|
129
129
|
return False
|
130
130
|
|
131
131
|
|
132
|
-
def should_show_item(item_name, item_path, is_directory):
|
132
|
+
def should_show_item(item_name, item_path, is_directory):
|
133
133
|
"""Determine if an item should be shown based on filtering rules"""
|
134
134
|
# Always hide system files
|
135
135
|
if item_name in {".DS_Store", "Thumbs.db", "desktop.ini"}:
|
@@ -29,7 +29,7 @@ class AgentProfileGenerator:
|
|
29
29
|
if not self.template_path.exists():
|
30
30
|
raise FileNotFoundError(f"Template not found: {self.template_path}")
|
31
31
|
|
32
|
-
with
|
32
|
+
with self.template_path.open() as f:
|
33
33
|
return yaml.safe_load(f)
|
34
34
|
|
35
35
|
def generate_profile(self, config: Dict[str, Any]) -> str:
|
@@ -284,11 +284,11 @@ class EventHandlers:
|
|
284
284
|
# Log the agent prompt asynchronously
|
285
285
|
try:
|
286
286
|
loop = asyncio.get_running_loop()
|
287
|
-
asyncio.create_task(
|
287
|
+
_task = asyncio.create_task( # noqa: RUF006
|
288
288
|
log_manager.log_prompt(
|
289
289
|
f"agent_{agent_type}", prompt_content, metadata
|
290
290
|
)
|
291
|
-
)
|
291
|
+
) # Fire-and-forget logging (ephemeral hook process)
|
292
292
|
except RuntimeError:
|
293
293
|
# No running loop, create one
|
294
294
|
loop = asyncio.new_event_loop()
|
@@ -428,7 +428,7 @@ main "$@"
|
|
428
428
|
return
|
429
429
|
|
430
430
|
try:
|
431
|
-
with
|
431
|
+
with self.old_settings_file.open() as f:
|
432
432
|
old_settings = json.load(f)
|
433
433
|
|
434
434
|
# Remove hooks section if present
|
@@ -437,7 +437,7 @@ main "$@"
|
|
437
437
|
self.logger.info(f"Removing hooks from {self.old_settings_file}")
|
438
438
|
|
439
439
|
# Write back the cleaned settings
|
440
|
-
with
|
440
|
+
with self.old_settings_file.open("w") as f:
|
441
441
|
json.dump(old_settings, f, indent=2)
|
442
442
|
|
443
443
|
self.logger.info(f"Cleaned up hooks from {self.old_settings_file}")
|
@@ -450,7 +450,7 @@ main "$@"
|
|
450
450
|
|
451
451
|
# Load existing settings.json or create new
|
452
452
|
if self.settings_file.exists():
|
453
|
-
with
|
453
|
+
with self.settings_file.open() as f:
|
454
454
|
settings = json.load(f)
|
455
455
|
self.logger.info(f"Found existing Claude settings at {self.settings_file}")
|
456
456
|
else:
|
@@ -490,7 +490,7 @@ main "$@"
|
|
490
490
|
]
|
491
491
|
|
492
492
|
# Write settings to settings.json
|
493
|
-
with
|
493
|
+
with self.settings_file.open("w") as f:
|
494
494
|
json.dump(settings, f, indent=2)
|
495
495
|
|
496
496
|
self.logger.info(f"Updated Claude settings at {self.settings_file}")
|
@@ -564,7 +564,7 @@ main "$@"
|
|
564
564
|
issues.append(f"Claude settings file not found at {self.settings_file}")
|
565
565
|
else:
|
566
566
|
try:
|
567
|
-
with
|
567
|
+
with self.settings_file.open() as f:
|
568
568
|
settings = json.load(f)
|
569
569
|
|
570
570
|
if "hooks" not in settings:
|
@@ -615,7 +615,7 @@ main "$@"
|
|
615
615
|
# Remove from Claude settings (both old and new locations)
|
616
616
|
for settings_path in [self.settings_file, self.old_settings_file]:
|
617
617
|
if settings_path.exists():
|
618
|
-
with open(
|
618
|
+
with settings_path.open() as f:
|
619
619
|
settings = json.load(f)
|
620
620
|
|
621
621
|
if "hooks" in settings:
|
@@ -655,7 +655,7 @@ main "$@"
|
|
655
655
|
del settings["hooks"]
|
656
656
|
|
657
657
|
# Write back settings
|
658
|
-
with open(
|
658
|
+
with settings_path.open("w") as f:
|
659
659
|
json.dump(settings, f, indent=2)
|
660
660
|
|
661
661
|
self.logger.info(f"Removed hooks from {settings_path}")
|
@@ -709,7 +709,7 @@ main "$@"
|
|
709
709
|
|
710
710
|
if self.settings_file.exists():
|
711
711
|
try:
|
712
|
-
with
|
712
|
+
with self.settings_file.open() as f:
|
713
713
|
settings = json.load(f)
|
714
714
|
if "hooks" in settings:
|
715
715
|
status["configured_events"] = list(settings["hooks"].keys())
|
@@ -720,7 +720,7 @@ main "$@"
|
|
720
720
|
# Also check old settings file
|
721
721
|
if self.old_settings_file.exists():
|
722
722
|
try:
|
723
|
-
with
|
723
|
+
with self.old_settings_file.open() as f:
|
724
724
|
old_settings = json.load(f)
|
725
725
|
if "hooks" in old_settings:
|
726
726
|
status["old_file_has_hooks"] = True
|
@@ -76,6 +76,9 @@ class ConnectionManagerService:
|
|
76
76
|
# For backward compatibility with tests
|
77
77
|
self.connection_pool = None # No longer used
|
78
78
|
|
79
|
+
# Track async emit tasks to prevent garbage collection
|
80
|
+
self._emit_tasks: set = set()
|
81
|
+
|
79
82
|
if DEBUG:
|
80
83
|
print(
|
81
84
|
f"✅ HTTP connection manager initialized - endpoint: {self.http_endpoint}",
|
@@ -141,8 +144,10 @@ class ConnectionManagerService:
|
|
141
144
|
pass
|
142
145
|
|
143
146
|
if loop:
|
144
|
-
# We're in an async context, create a task
|
145
|
-
loop.create_task(self._async_emit(namespace, event, data))
|
147
|
+
# We're in an async context, create a task with tracking
|
148
|
+
task = loop.create_task(self._async_emit(namespace, event, data))
|
149
|
+
self._emit_tasks.add(task)
|
150
|
+
task.add_done_callback(self._emit_tasks.discard)
|
146
151
|
# Don't wait for completion to maintain low latency
|
147
152
|
if DEBUG:
|
148
153
|
print(f"✅ Async emit scheduled: {event}", file=sys.stderr)
|
@@ -138,7 +138,7 @@ def summarize_todos(todos: list) -> dict:
|
|
138
138
|
}
|
139
139
|
|
140
140
|
|
141
|
-
def classify_tool_operation(tool_name: str, tool_input: dict) -> str:
|
141
|
+
def classify_tool_operation(tool_name: str, tool_input: dict) -> str:
|
142
142
|
"""Classify the type of operation being performed."""
|
143
143
|
if tool_name in ["Read", "LS", "Glob", "Grep", "NotebookRead"]:
|
144
144
|
return "read"
|
@@ -155,7 +155,7 @@ def classify_tool_operation(tool_name: str, tool_input: dict) -> str: # noqa: P
|
|
155
155
|
return "other"
|
156
156
|
|
157
157
|
|
158
|
-
def assess_security_risk(tool_name: str, tool_input: dict) -> str:
|
158
|
+
def assess_security_risk(tool_name: str, tool_input: dict) -> str:
|
159
159
|
"""Assess the security risk level of the tool operation."""
|
160
160
|
if tool_name == "Bash":
|
161
161
|
command = tool_input.get("command", "").lower()
|
@@ -234,7 +234,7 @@ class MemoryPostDelegationHook(PostDelegationHook):
|
|
234
234
|
"context": "context", # Current Technical Context
|
235
235
|
}
|
236
236
|
|
237
|
-
def execute(self, context: HookContext) -> HookResult:
|
237
|
+
def execute(self, context: HookContext) -> HookResult:
|
238
238
|
"""Extract and store learnings from delegation result.
|
239
239
|
|
240
240
|
WHY: Capturing learnings immediately after task completion ensures we
|
@@ -118,7 +118,7 @@ async def validate_agent_dependencies(profile_path: Path) -> ValidationResult:
|
|
118
118
|
result = ValidationResult(is_valid=True)
|
119
119
|
|
120
120
|
try:
|
121
|
-
with open(
|
121
|
+
with profile_path.open() as f:
|
122
122
|
profile_data = yaml.safe_load(f)
|
123
123
|
|
124
124
|
# Check for circular dependencies
|
claude_mpm/init.py
CHANGED
@@ -270,11 +270,11 @@ class ProjectInitializer:
|
|
270
270
|
"""
|
271
271
|
try:
|
272
272
|
# Read existing JSON configuration
|
273
|
-
with open(
|
273
|
+
with old_file.open() as f:
|
274
274
|
config = json.load(f)
|
275
275
|
|
276
276
|
# Write as YAML
|
277
|
-
with open(
|
277
|
+
with new_file.open("w") as f:
|
278
278
|
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
279
279
|
|
280
280
|
self.logger.info(
|
@@ -310,7 +310,7 @@ class ProjectInitializer:
|
|
310
310
|
},
|
311
311
|
}
|
312
312
|
|
313
|
-
with open(
|
313
|
+
with config_file.open("w") as f:
|
314
314
|
yaml.dump(default_config, f, default_flow_style=False, sort_keys=False)
|
315
315
|
|
316
316
|
def _create_project_config(self, config_file: Path):
|
@@ -322,7 +322,7 @@ class ProjectInitializer:
|
|
322
322
|
"tickets": {"auto_create": True, "prefix": "TSK"},
|
323
323
|
}
|
324
324
|
|
325
|
-
with open(
|
325
|
+
with config_file.open("w") as f:
|
326
326
|
json.dump(project_config, f, indent=2)
|
327
327
|
|
328
328
|
def _copy_agent_templates(self):
|
@@ -448,7 +448,7 @@ class AgentSession:
|
|
448
448
|
filepath = directory / filename
|
449
449
|
|
450
450
|
# Save to file
|
451
|
-
with open(
|
451
|
+
with filepath.open("w", encoding="utf-8") as f:
|
452
452
|
json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
|
453
453
|
|
454
454
|
return str(filepath)
|
@@ -37,7 +37,7 @@ def is_running(pid_file: Path) -> bool:
|
|
37
37
|
return False
|
38
38
|
|
39
39
|
try:
|
40
|
-
with open(
|
40
|
+
with pid_file.open() as f:
|
41
41
|
pid = int(f.read().strip())
|
42
42
|
|
43
43
|
# Check if process exists
|
@@ -85,7 +85,7 @@ def start_server(port: int = DEFAULT_PORT, daemon: bool = True) -> bool:
|
|
85
85
|
logger.info(f"Socket.IO daemon started on port {actual_port}")
|
86
86
|
# Save the port for clients to discover
|
87
87
|
port_file = pid_file.parent / "socketio-port"
|
88
|
-
with open(
|
88
|
+
with port_file.open("w") as f:
|
89
89
|
f.write(str(actual_port))
|
90
90
|
else:
|
91
91
|
logger.error("Failed to start Socket.IO daemon")
|
@@ -102,7 +102,7 @@ def stop_server() -> bool:
|
|
102
102
|
return False
|
103
103
|
|
104
104
|
try:
|
105
|
-
with open(
|
105
|
+
with pid_file.open() as f:
|
106
106
|
pid = int(f.read().strip())
|
107
107
|
|
108
108
|
# Send SIGTERM for graceful shutdown
|
@@ -155,12 +155,12 @@ def status_server() -> bool:
|
|
155
155
|
|
156
156
|
if is_running(pid_file):
|
157
157
|
try:
|
158
|
-
with open(
|
158
|
+
with pid_file.open() as f:
|
159
159
|
pid = int(f.read().strip())
|
160
160
|
|
161
161
|
port = DEFAULT_PORT
|
162
162
|
if port_file.exists():
|
163
|
-
with open(
|
163
|
+
with port_file.open() as f:
|
164
164
|
port = int(f.read().strip())
|
165
165
|
|
166
166
|
print(f"Socket.IO daemon is running (PID: {pid}, Port: {port})")
|
claude_mpm/services/__init__.py
CHANGED
@@ -144,8 +144,8 @@ def __getattr__(name):
|
|
144
144
|
try:
|
145
145
|
module = import_module("claude_mpm.services.recovery_manager")
|
146
146
|
return module.RecoveryManager
|
147
|
-
except ImportError:
|
148
|
-
raise AttributeError(f"Recovery management not available: {name}")
|
147
|
+
except ImportError as e:
|
148
|
+
raise AttributeError(f"Recovery management not available: {name}") from e
|
149
149
|
|
150
150
|
# Handle MCP interfaces (names starting with "IMCP")
|
151
151
|
if name.startswith("IMCP"):
|
@@ -215,7 +215,7 @@ class AgentCapabilitiesService(BaseService, AgentCapabilitiesInterface):
|
|
215
215
|
self.logger.debug(f"Could not parse agent {agent_file}: {e}")
|
216
216
|
continue
|
217
217
|
|
218
|
-
def _categorize_agent(self, agent_id: str, content: str) -> str:
|
218
|
+
def _categorize_agent(self, agent_id: str, content: str) -> str:
|
219
219
|
"""Categorize an agent based on its ID and content."""
|
220
220
|
agent_id_lower = agent_id.lower()
|
221
221
|
content_lower = content.lower()
|