claude-mpm 4.5.11__py3-none-any.whl → 4.5.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/frontmatter_validator.py +4 -4
- 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 +4 -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 +4 -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 +7 -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 +3 -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 +13 -16
- claude_mpm/services/port_manager.py +2 -2
- claude_mpm/services/project/analyzer.py +3 -3
- claude_mpm/services/project/archive_manager.py +13 -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 +8 -8
- claude_mpm/services/project/registry.py +4 -4
- claude_mpm/services/project_port_allocator.py +7 -11
- 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 +6 -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 +9 -9
- claude_mpm/services/unified/deployment_strategies/utils.py +9 -9
- claude_mpm/services/unified/deployment_strategies/vercel.py +7 -7
- 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.12.dist-info}/METADATA +1 -1
- {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.12.dist-info}/RECORD +183 -183
- {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.12.dist-info}/WHEEL +0 -0
- {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.12.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.12.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.12.dist-info}/top_level.txt +0 -0
|
@@ -64,7 +64,7 @@ class ExperimentalFeatures:
|
|
|
64
64
|
"""
|
|
65
65
|
if self._config_file and self._config_file.exists():
|
|
66
66
|
try:
|
|
67
|
-
with
|
|
67
|
+
with self._config_file.open() as f:
|
|
68
68
|
config = json.load(f)
|
|
69
69
|
experimental = config.get("experimental_features", {})
|
|
70
70
|
self._features.update(experimental)
|
|
@@ -125,7 +125,7 @@ class ExperimentalFeatures:
|
|
|
125
125
|
accepted_file = Path.home() / ".claude-mpm" / ".experimental_accepted"
|
|
126
126
|
if accepted_file.exists():
|
|
127
127
|
try:
|
|
128
|
-
with open(
|
|
128
|
+
with accepted_file.open() as f:
|
|
129
129
|
accepted = json.load(f)
|
|
130
130
|
if feature in accepted.get("features", []):
|
|
131
131
|
return False
|
|
@@ -148,7 +148,7 @@ class ExperimentalFeatures:
|
|
|
148
148
|
|
|
149
149
|
try:
|
|
150
150
|
if accepted_file.exists():
|
|
151
|
-
with open(
|
|
151
|
+
with accepted_file.open() as f:
|
|
152
152
|
data = json.load(f)
|
|
153
153
|
else:
|
|
154
154
|
data = {"features": [], "timestamp": {}}
|
|
@@ -159,7 +159,7 @@ class ExperimentalFeatures:
|
|
|
159
159
|
"CLAUDE_MPM_TIMESTAMP", str(Path.cwd())
|
|
160
160
|
)
|
|
161
161
|
|
|
162
|
-
with open(
|
|
162
|
+
with accepted_file.open("w") as f:
|
|
163
163
|
json.dump(data, f, indent=2)
|
|
164
164
|
except Exception:
|
|
165
165
|
# Silently ignore errors in acceptance tracking
|
|
@@ -249,7 +249,7 @@ class ConfigManager:
|
|
|
249
249
|
for config_path in self.config_search_paths:
|
|
250
250
|
if config_path.exists():
|
|
251
251
|
try:
|
|
252
|
-
with open(
|
|
252
|
+
with config_path.open() as f:
|
|
253
253
|
return json.load(f)
|
|
254
254
|
except Exception as e:
|
|
255
255
|
print(f"Warning: Failed to load config from {config_path}: {e}")
|
|
@@ -267,7 +267,7 @@ class ConfigManager:
|
|
|
267
267
|
path = config_dir / self.config_file_name
|
|
268
268
|
|
|
269
269
|
try:
|
|
270
|
-
with open(
|
|
270
|
+
with path.open("w") as f:
|
|
271
271
|
json.dump(config.to_dict(), f, indent=2)
|
|
272
272
|
return True
|
|
273
273
|
except Exception as e:
|
|
@@ -209,7 +209,7 @@ class AgentSessionManager:
|
|
|
209
209
|
"agent_sessions": dict(self.agent_sessions),
|
|
210
210
|
"updated_at": datetime.now(timezone.utc).isoformat(),
|
|
211
211
|
}
|
|
212
|
-
with open(
|
|
212
|
+
with session_file.open("w") as f:
|
|
213
213
|
json.dump(data, f, indent=2)
|
|
214
214
|
except Exception as e:
|
|
215
215
|
logger.error(f"Failed to save agent sessions: {e}")
|
|
@@ -219,7 +219,7 @@ class AgentSessionManager:
|
|
|
219
219
|
session_file = self.session_dir / "agent_sessions.json"
|
|
220
220
|
if session_file.exists():
|
|
221
221
|
try:
|
|
222
|
-
with open(
|
|
222
|
+
with session_file.open() as f:
|
|
223
223
|
data = json.load(f)
|
|
224
224
|
self.agent_sessions = defaultdict(
|
|
225
225
|
dict, data.get("agent_sessions", {})
|
claude_mpm/core/api_validator.py
CHANGED
|
@@ -84,7 +84,7 @@ class APIKeyValidator:
|
|
|
84
84
|
|
|
85
85
|
return not bool(self.errors), self.errors, self.warnings
|
|
86
86
|
|
|
87
|
-
def _validate_openai_key(self, api_key: str) -> bool:
|
|
87
|
+
def _validate_openai_key(self, api_key: str) -> bool:
|
|
88
88
|
"""Validate OpenAI API key.
|
|
89
89
|
|
|
90
90
|
Args:
|
|
@@ -133,7 +133,7 @@ class APIKeyValidator:
|
|
|
133
133
|
self.errors.append(f"❌ OpenAI API validation failed with error: {e}")
|
|
134
134
|
return False
|
|
135
135
|
|
|
136
|
-
def _validate_anthropic_key(self, api_key: str) -> bool:
|
|
136
|
+
def _validate_anthropic_key(self, api_key: str) -> bool:
|
|
137
137
|
"""Validate Anthropic API key.
|
|
138
138
|
|
|
139
139
|
Args:
|
|
@@ -196,7 +196,7 @@ class APIKeyValidator:
|
|
|
196
196
|
self.errors.append(f"❌ Anthropic API validation failed with error: {e}")
|
|
197
197
|
return False
|
|
198
198
|
|
|
199
|
-
def _validate_github_token(self, token: str) -> bool:
|
|
199
|
+
def _validate_github_token(self, token: str) -> bool:
|
|
200
200
|
"""Validate GitHub personal access token.
|
|
201
201
|
|
|
202
202
|
Args:
|
claude_mpm/core/base_service.py
CHANGED
|
@@ -428,7 +428,16 @@ class BaseService(LoggerMixin, ABC):
|
|
|
428
428
|
self.logger.info(
|
|
429
429
|
f"Received signal {signum}, initiating graceful shutdown..."
|
|
430
430
|
)
|
|
431
|
-
|
|
431
|
+
# Get the event loop and create a tracked shutdown task
|
|
432
|
+
try:
|
|
433
|
+
loop = asyncio.get_event_loop()
|
|
434
|
+
task = loop.create_task(self.stop())
|
|
435
|
+
# Store reference to prevent GC during shutdown
|
|
436
|
+
if not hasattr(self, '_shutdown_task'):
|
|
437
|
+
self._shutdown_task = task
|
|
438
|
+
except RuntimeError:
|
|
439
|
+
# No event loop, call stop synchronously
|
|
440
|
+
self.logger.warning("No event loop available for graceful shutdown")
|
|
432
441
|
|
|
433
442
|
signal.signal(signal.SIGINT, signal_handler)
|
|
434
443
|
signal.signal(signal.SIGTERM, signal_handler)
|
claude_mpm/core/cache.py
CHANGED
|
@@ -391,7 +391,7 @@ class FileSystemCache:
|
|
|
391
391
|
|
|
392
392
|
try:
|
|
393
393
|
self.persist_path.parent.mkdir(parents=True, exist_ok=True)
|
|
394
|
-
with
|
|
394
|
+
with self.persist_path.open("wb") as f:
|
|
395
395
|
pickle.dump(self._cache, f)
|
|
396
396
|
self._logger.debug(f"Cache persisted to {self.persist_path}")
|
|
397
397
|
except Exception as e:
|
|
@@ -403,7 +403,7 @@ class FileSystemCache:
|
|
|
403
403
|
return
|
|
404
404
|
|
|
405
405
|
try:
|
|
406
|
-
with
|
|
406
|
+
with self.persist_path.open("rb") as f:
|
|
407
407
|
loaded_cache = pickle.load(f)
|
|
408
408
|
|
|
409
409
|
# Rebuild cache with validation
|
claude_mpm/core/config.py
CHANGED
|
@@ -253,7 +253,7 @@ class Config:
|
|
|
253
253
|
"operation": "read",
|
|
254
254
|
"error_type": type(e).__name__,
|
|
255
255
|
},
|
|
256
|
-
)
|
|
256
|
+
) from e
|
|
257
257
|
except Exception as e:
|
|
258
258
|
# Catch any remaining unexpected errors and wrap them as configuration errors
|
|
259
259
|
raise ConfigurationError(
|
|
@@ -263,7 +263,7 @@ class Config:
|
|
|
263
263
|
"error_type": type(e).__name__,
|
|
264
264
|
"original_error": str(e),
|
|
265
265
|
},
|
|
266
|
-
)
|
|
266
|
+
) from e
|
|
267
267
|
|
|
268
268
|
def _load_env_vars(self) -> None:
|
|
269
269
|
"""Load configuration from environment variables."""
|
|
@@ -651,7 +651,7 @@ class Config:
|
|
|
651
651
|
"format": format,
|
|
652
652
|
"error_type": type(e).__name__,
|
|
653
653
|
},
|
|
654
|
-
)
|
|
654
|
+
) from e
|
|
655
655
|
except Exception as e:
|
|
656
656
|
# Re-raise ConfigurationError as-is, wrap others
|
|
657
657
|
if isinstance(e, ConfigurationError):
|
|
@@ -663,7 +663,7 @@ class Config:
|
|
|
663
663
|
"format": format,
|
|
664
664
|
"error_type": type(e).__name__,
|
|
665
665
|
},
|
|
666
|
-
)
|
|
666
|
+
) from e
|
|
667
667
|
|
|
668
668
|
def validate(self, schema: Dict[str, Any]) -> bool:
|
|
669
669
|
"""
|
|
@@ -88,7 +88,7 @@ class ConfigAliasManager:
|
|
|
88
88
|
self.config_mgr.save_json(aliases, self.aliases_file, sort_keys=True)
|
|
89
89
|
except Exception as e:
|
|
90
90
|
logger.error(f"Failed to save aliases: {e}")
|
|
91
|
-
raise ConfigAliasError(f"Failed to save aliases: {e}")
|
|
91
|
+
raise ConfigAliasError(f"Failed to save aliases: {e}") from e
|
|
92
92
|
|
|
93
93
|
def create_alias(self, alias_name: str, directory_path: str) -> None:
|
|
94
94
|
"""
|
|
@@ -211,7 +211,7 @@ class ConfigAliasManager:
|
|
|
211
211
|
except Exception as e:
|
|
212
212
|
raise InvalidDirectoryError(
|
|
213
213
|
f"Cannot create directory '{directory_path}': {e}"
|
|
214
|
-
)
|
|
214
|
+
) from e
|
|
215
215
|
|
|
216
216
|
# Verify we can write to the directory
|
|
217
217
|
test_file = directory_path / ".claude_pm_test"
|
|
@@ -221,14 +221,14 @@ class ConfigAliasManager:
|
|
|
221
221
|
except Exception as e:
|
|
222
222
|
raise InvalidDirectoryError(
|
|
223
223
|
f"Directory '{directory_path}' is not writable: {e}"
|
|
224
|
-
)
|
|
224
|
+
) from e
|
|
225
225
|
|
|
226
226
|
return directory_path
|
|
227
227
|
|
|
228
228
|
except InvalidDirectoryError:
|
|
229
229
|
raise
|
|
230
230
|
except Exception as e:
|
|
231
|
-
raise InvalidDirectoryError(f"Invalid directory path '{path}': {e}")
|
|
231
|
+
raise InvalidDirectoryError(f"Invalid directory path '{path}': {e}") from e
|
|
232
232
|
|
|
233
233
|
def get_alias(self, alias_name: str) -> Optional[str]:
|
|
234
234
|
"""
|
claude_mpm/core/error_handler.py
CHANGED
|
@@ -218,7 +218,7 @@ class ErrorHandler:
|
|
|
218
218
|
return handler(error, error_context)
|
|
219
219
|
except Exception as recovery_error:
|
|
220
220
|
self.logger.error(f"Recovery failed: {recovery_error}")
|
|
221
|
-
raise error
|
|
221
|
+
raise error from recovery_error
|
|
222
222
|
|
|
223
223
|
# No recovery handler available
|
|
224
224
|
self.logger.warning(f"No recovery handler for {error_type.__name__}")
|
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()
|