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
@@ -287,7 +287,7 @@ class AgentBuilderService:
|
|
287
287
|
|
288
288
|
for template_file in self.templates_dir.glob("*.json"):
|
289
289
|
try:
|
290
|
-
with open(
|
290
|
+
with template_file.open() as f:
|
291
291
|
config = json.load(f)
|
292
292
|
|
293
293
|
# Use filename stem as ID if not specified in config
|
@@ -377,12 +377,14 @@ class AgentBuilderService:
|
|
377
377
|
raise AgentDeploymentError(f"Template '{template_id}' not found")
|
378
378
|
|
379
379
|
try:
|
380
|
-
with open(
|
380
|
+
with template_file.open() as f:
|
381
381
|
config = json.load(f)
|
382
382
|
self._template_cache[template_id] = config
|
383
383
|
return config.copy()
|
384
384
|
except Exception as e:
|
385
|
-
raise AgentDeploymentError(
|
385
|
+
raise AgentDeploymentError(
|
386
|
+
f"Failed to load template '{template_id}': {e}"
|
387
|
+
) from e
|
386
388
|
|
387
389
|
def _load_instructions(self, agent_id: str) -> str:
|
388
390
|
"""Load agent instructions.
|
@@ -406,7 +408,7 @@ class AgentBuilderService:
|
|
406
408
|
for instructions_file in possible_files:
|
407
409
|
if instructions_file.exists():
|
408
410
|
try:
|
409
|
-
with open(
|
411
|
+
with instructions_file.open() as f:
|
410
412
|
return f.read()
|
411
413
|
except Exception as e:
|
412
414
|
self.logger.warning(
|
@@ -272,7 +272,7 @@ class AgentLifecycleManager(BaseService):
|
|
272
272
|
# First convert to JSON string with custom encoder, then save
|
273
273
|
json_str = json.dumps(data, indent=2, default=str)
|
274
274
|
records_file.parent.mkdir(parents=True, exist_ok=True)
|
275
|
-
with open(
|
275
|
+
with records_file.open("w", encoding="utf-8") as f:
|
276
276
|
f.write(json_str)
|
277
277
|
|
278
278
|
self.logger.debug(f"Saved {len(self.agent_records)} agent records")
|
@@ -227,7 +227,7 @@ class AgentMetricsCollector:
|
|
227
227
|
return "validation_error"
|
228
228
|
return "other_error"
|
229
229
|
|
230
|
-
def _extract_agent_type(self, agent_name: str) -> str:
|
230
|
+
def _extract_agent_type(self, agent_name: str) -> str:
|
231
231
|
"""
|
232
232
|
Extract agent type from agent name for categorization.
|
233
233
|
|
@@ -80,7 +80,7 @@ class AgentRecordService(BaseService):
|
|
80
80
|
json_str = json.dumps(data, indent=2, default=str)
|
81
81
|
self.records_file.parent.mkdir(parents=True, exist_ok=True)
|
82
82
|
|
83
|
-
with
|
83
|
+
with self.records_file.open("w", encoding="utf-8") as f:
|
84
84
|
f.write(json_str)
|
85
85
|
|
86
86
|
self.logger.debug(f"Saved {len(records)} agent records")
|
@@ -149,7 +149,7 @@ class AgentRecordService(BaseService):
|
|
149
149
|
json_str = json.dumps(data, indent=2, default=str)
|
150
150
|
self.history_file.parent.mkdir(parents=True, exist_ok=True)
|
151
151
|
|
152
|
-
with
|
152
|
+
with self.history_file.open("w", encoding="utf-8") as f:
|
153
153
|
f.write(json_str)
|
154
154
|
|
155
155
|
self.logger.debug(f"Saved {len(history)} operation history entries")
|
@@ -255,7 +255,7 @@ class AgentRecordService(BaseService):
|
|
255
255
|
|
256
256
|
# Write to output path
|
257
257
|
json_str = json.dumps(data, indent=2, default=str)
|
258
|
-
with open(
|
258
|
+
with output_path.open("w", encoding="utf-8") as f:
|
259
259
|
f.write(json_str)
|
260
260
|
|
261
261
|
elif format == "csv":
|
@@ -48,10 +48,10 @@ class TargetDirectorySetupStep(BaseDeploymentStep):
|
|
48
48
|
try:
|
49
49
|
test_file.write_text("test")
|
50
50
|
test_file.unlink()
|
51
|
-
except Exception:
|
51
|
+
except Exception as e:
|
52
52
|
raise PermissionError(
|
53
53
|
f"Target directory is not writable: {context.actual_target_dir}"
|
54
|
-
)
|
54
|
+
) from e
|
55
55
|
|
56
56
|
self.logger.info(f"Target directory set up: {context.actual_target_dir}")
|
57
57
|
|
@@ -457,7 +457,7 @@ class AgentProfileLoader(BaseService):
|
|
457
457
|
|
458
458
|
if prompt_file.exists():
|
459
459
|
try:
|
460
|
-
with open(
|
460
|
+
with prompt_file.open() as f:
|
461
461
|
data = json.load(f)
|
462
462
|
for prompt_data in data:
|
463
463
|
prompt = ImprovedPrompt(
|
@@ -500,7 +500,7 @@ class AgentProfileLoader(BaseService):
|
|
500
500
|
|
501
501
|
# Save to file
|
502
502
|
prompt_file = self.improved_prompts_path / f"{agent_name}_prompts.json"
|
503
|
-
with open(
|
503
|
+
with prompt_file.open("w") as f:
|
504
504
|
json.dump(
|
505
505
|
[
|
506
506
|
{
|
@@ -195,7 +195,7 @@ class LocalAgentTemplateManager:
|
|
195
195
|
"""
|
196
196
|
for template_file in directory.glob("*.json"):
|
197
197
|
try:
|
198
|
-
with open(
|
198
|
+
with template_file.open() as f:
|
199
199
|
data = json.load(f)
|
200
200
|
|
201
201
|
# Create LocalAgentTemplate
|
@@ -307,7 +307,7 @@ class LocalAgentTemplateManager:
|
|
307
307
|
|
308
308
|
# Save to JSON file
|
309
309
|
template_file = target_dir / f"{template.agent_id}.json"
|
310
|
-
with open(
|
310
|
+
with template_file.open("w") as f:
|
311
311
|
json.dump(template.to_json(), f, indent=2)
|
312
312
|
|
313
313
|
# Invalidate cache
|
@@ -628,7 +628,7 @@ class LocalAgentTemplateManager:
|
|
628
628
|
|
629
629
|
# Save current version
|
630
630
|
old_version_file = versions_dir / f"{template.agent_version}.json"
|
631
|
-
with open(
|
631
|
+
with old_version_file.open("w") as f:
|
632
632
|
json.dump(template.to_json(), f, indent=2)
|
633
633
|
|
634
634
|
# Update template version
|
@@ -691,7 +691,7 @@ class LocalAgentTemplateManager:
|
|
691
691
|
count = 0
|
692
692
|
for agent_id, template in templates.items():
|
693
693
|
output_file = output_dir / f"{agent_id}.json"
|
694
|
-
with open(
|
694
|
+
with output_file.open("w") as f:
|
695
695
|
json.dump(template.to_json(), f, indent=2)
|
696
696
|
count += 1
|
697
697
|
|
@@ -715,7 +715,7 @@ class LocalAgentTemplateManager:
|
|
715
715
|
count = 0
|
716
716
|
for template_file in input_dir.glob("*.json"):
|
717
717
|
try:
|
718
|
-
with open(
|
718
|
+
with template_file.open() as f:
|
719
719
|
data = json.load(f)
|
720
720
|
|
721
721
|
template = LocalAgentTemplate.from_json(data)
|
@@ -166,7 +166,7 @@ class DeployedAgentDiscovery(ConfigServiceBase):
|
|
166
166
|
try:
|
167
167
|
path = Path(agent_path)
|
168
168
|
if path.exists() and path.suffix == ".json":
|
169
|
-
with open(
|
169
|
+
with path.open() as f:
|
170
170
|
return json.load(f)
|
171
171
|
except Exception as e:
|
172
172
|
logger.warning(f"Failed to load full agent data from {agent_path}: {e}")
|
@@ -156,12 +156,19 @@ class AgentFileSystemHandler(FileSystemEventHandler):
|
|
156
156
|
def __init__(self, tracker: "AgentModificationTracker"):
|
157
157
|
self.tracker = tracker
|
158
158
|
|
159
|
+
def _create_tracked_task(self, coro):
|
160
|
+
"""Create a task with automatic tracking and cleanup."""
|
161
|
+
task = asyncio.create_task(coro)
|
162
|
+
self.tracker._file_event_tasks.add(task)
|
163
|
+
task.add_done_callback(self.tracker._file_event_tasks.discard)
|
164
|
+
return task
|
165
|
+
|
159
166
|
def on_created(self, event: FileSystemEvent) -> None:
|
160
167
|
"""Handle file creation events."""
|
161
168
|
if not event.is_directory and event.src_path.endswith(
|
162
169
|
(".md", ".json", ".yaml")
|
163
170
|
):
|
164
|
-
|
171
|
+
self._create_tracked_task(
|
165
172
|
self.tracker._handle_file_modification(
|
166
173
|
event.src_path, ModificationType.CREATE
|
167
174
|
)
|
@@ -172,7 +179,7 @@ class AgentFileSystemHandler(FileSystemEventHandler):
|
|
172
179
|
if not event.is_directory and event.src_path.endswith(
|
173
180
|
(".md", ".json", ".yaml")
|
174
181
|
):
|
175
|
-
|
182
|
+
self._create_tracked_task(
|
176
183
|
self.tracker._handle_file_modification(
|
177
184
|
event.src_path, ModificationType.MODIFY
|
178
185
|
)
|
@@ -183,7 +190,7 @@ class AgentFileSystemHandler(FileSystemEventHandler):
|
|
183
190
|
if not event.is_directory and event.src_path.endswith(
|
184
191
|
(".md", ".json", ".yaml")
|
185
192
|
):
|
186
|
-
|
193
|
+
self._create_tracked_task(
|
187
194
|
self.tracker._handle_file_modification(
|
188
195
|
event.src_path, ModificationType.DELETE
|
189
196
|
)
|
@@ -194,7 +201,7 @@ class AgentFileSystemHandler(FileSystemEventHandler):
|
|
194
201
|
if not event.is_directory and event.src_path.endswith(
|
195
202
|
(".md", ".json", ".yaml")
|
196
203
|
):
|
197
|
-
|
204
|
+
self._create_tracked_task(
|
198
205
|
self.tracker._handle_file_move(event.src_path, event.dest_path)
|
199
206
|
)
|
200
207
|
|
@@ -248,6 +255,7 @@ class AgentModificationTracker(BaseService):
|
|
248
255
|
# Background tasks
|
249
256
|
self._persistence_task: Optional[asyncio.Task] = None
|
250
257
|
self._cleanup_task: Optional[asyncio.Task] = None
|
258
|
+
self._file_event_tasks: Set[asyncio.Task] = set() # Track file event tasks
|
251
259
|
|
252
260
|
# Callbacks
|
253
261
|
self.modification_callbacks: List[Callable[[AgentModification], None]] = []
|
@@ -473,7 +481,7 @@ class AgentModificationTracker(BaseService):
|
|
473
481
|
metadata["file_size_after"] = path.stat().st_size
|
474
482
|
|
475
483
|
# File hash
|
476
|
-
with open(
|
484
|
+
with path.open("rb") as f:
|
477
485
|
metadata["file_hash_after"] = hashlib.sha256(f.read()).hexdigest()
|
478
486
|
|
479
487
|
# File type
|
@@ -512,7 +520,7 @@ class AgentModificationTracker(BaseService):
|
|
512
520
|
}
|
513
521
|
|
514
522
|
metadata_path = backup_dir / "metadata.json"
|
515
|
-
with open(
|
523
|
+
with metadata_path.open("w") as f:
|
516
524
|
json.dump(metadata, f, indent=2)
|
517
525
|
|
518
526
|
return str(backup_path)
|
@@ -661,7 +669,7 @@ class AgentModificationTracker(BaseService):
|
|
661
669
|
# Load active modifications
|
662
670
|
active_path = self.persistence_root / "active_modifications.json"
|
663
671
|
if active_path.exists():
|
664
|
-
with open(
|
672
|
+
with active_path.open() as f:
|
665
673
|
data = json.load(f)
|
666
674
|
self.active_modifications = {
|
667
675
|
k: AgentModification.from_dict(v) for k, v in data.items()
|
@@ -669,7 +677,7 @@ class AgentModificationTracker(BaseService):
|
|
669
677
|
|
670
678
|
# Load modification history
|
671
679
|
for history_file in self.history_root.glob("*.json"):
|
672
|
-
with open(
|
680
|
+
with history_file.open() as f:
|
673
681
|
data = json.load(f)
|
674
682
|
agent_name = data["agent_name"]
|
675
683
|
history = ModificationHistory(agent_name=agent_name)
|
@@ -699,7 +707,7 @@ class AgentModificationTracker(BaseService):
|
|
699
707
|
active_data = {k: v.to_dict() for k, v in self.active_modifications.items()}
|
700
708
|
active_path = self.persistence_root / "active_modifications.json"
|
701
709
|
|
702
|
-
with open(
|
710
|
+
with active_path.open("w") as f:
|
703
711
|
json.dump(active_data, f, indent=2)
|
704
712
|
|
705
713
|
# Save modification history
|
@@ -714,7 +722,7 @@ class AgentModificationTracker(BaseService):
|
|
714
722
|
}
|
715
723
|
|
716
724
|
history_path = self.history_root / f"{agent_name}_history.json"
|
717
|
-
with open(
|
725
|
+
with history_path.open("w") as f:
|
718
726
|
json.dump(history_data, f, indent=2)
|
719
727
|
|
720
728
|
self.logger.debug("Saved modification history to disk")
|
@@ -767,7 +775,7 @@ class AgentModificationTracker(BaseService):
|
|
767
775
|
if backup_dir.is_dir():
|
768
776
|
metadata_path = backup_dir / "metadata.json"
|
769
777
|
if metadata_path.exists():
|
770
|
-
with open(
|
778
|
+
with metadata_path.open() as f:
|
771
779
|
metadata = json.load(f)
|
772
780
|
if metadata.get("backup_time", 0) < cutoff_time:
|
773
781
|
shutil.rmtree(backup_dir)
|
@@ -336,7 +336,7 @@ class AsyncSessionLogger:
|
|
336
336
|
with gzip.open(file_path, "wt", encoding="utf-8") as f:
|
337
337
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
338
338
|
else:
|
339
|
-
with open(
|
339
|
+
with file_path.open("w", encoding="utf-8") as f:
|
340
340
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
341
341
|
|
342
342
|
logger.debug(f"Wrote log entry to {file_path}")
|
@@ -224,7 +224,7 @@ class ClaudeSessionLogger:
|
|
224
224
|
|
225
225
|
# Save response
|
226
226
|
try:
|
227
|
-
with open(
|
227
|
+
with file_path.open("w", encoding="utf-8") as f:
|
228
228
|
json.dump(response_data, f, indent=2, ensure_ascii=False)
|
229
229
|
|
230
230
|
logger.debug(f"Logged response to {filename} for session {self.session_id}")
|
@@ -169,8 +169,8 @@ class AgentListingService(IAgentListingService):
|
|
169
169
|
|
170
170
|
base_service = AgentDeploymentService()
|
171
171
|
self._deployment_service = DeploymentServiceWrapper(base_service)
|
172
|
-
except ImportError:
|
173
|
-
raise ImportError("Agent deployment service not available")
|
172
|
+
except ImportError as e:
|
173
|
+
raise ImportError("Agent deployment service not available") from e
|
174
174
|
return self._deployment_service
|
175
175
|
|
176
176
|
@property
|
@@ -364,7 +364,7 @@ class AgentListingService(IAgentListingService):
|
|
364
364
|
if not agent_path.exists():
|
365
365
|
return None
|
366
366
|
|
367
|
-
with open(
|
367
|
+
with agent_path.open() as f:
|
368
368
|
content = f.read()
|
369
369
|
|
370
370
|
details = {
|
@@ -72,7 +72,7 @@ class AgentValidationService(IAgentValidationService):
|
|
72
72
|
self._registry = adapter.registry
|
73
73
|
except Exception as e:
|
74
74
|
self.logger.error(f"Failed to initialize agent registry: {e}")
|
75
|
-
raise RuntimeError(f"Could not initialize agent registry: {e}")
|
75
|
+
raise RuntimeError(f"Could not initialize agent registry: {e}") from e
|
76
76
|
return self._registry
|
77
77
|
|
78
78
|
def validate_agent(self, agent_name: str) -> Dict[str, Any]:
|
@@ -449,7 +449,7 @@ class SessionManager(ISessionManager):
|
|
449
449
|
sessions_dict = {
|
450
450
|
sid: session.to_dict() for sid, session in self._sessions_cache.items()
|
451
451
|
}
|
452
|
-
with open(
|
452
|
+
with session_file.open("w") as f:
|
453
453
|
json.dump(sessions_dict, f, indent=2)
|
454
454
|
except Exception as e:
|
455
455
|
self.logger.error(f"Failed to save sessions: {e}")
|
@@ -464,7 +464,7 @@ class SessionManager(ISessionManager):
|
|
464
464
|
return
|
465
465
|
|
466
466
|
try:
|
467
|
-
with open(
|
467
|
+
with session_file.open() as f:
|
468
468
|
sessions_dict = json.load(f)
|
469
469
|
|
470
470
|
self._sessions_cache = {
|
@@ -166,7 +166,7 @@ class PathResolver(IPathResolver):
|
|
166
166
|
self.logger.debug(f"No project root found from {start_path}")
|
167
167
|
return None
|
168
168
|
|
169
|
-
def detect_framework_path(self) -> Optional[Path]:
|
169
|
+
def detect_framework_path(self) -> Optional[Path]:
|
170
170
|
"""
|
171
171
|
Auto-detect claude-mpm framework using unified path management.
|
172
172
|
|
@@ -187,7 +187,7 @@ class ClaudeCodeCheck(BaseDiagnosticCheck):
|
|
187
187
|
|
188
188
|
# Check if it's up to date
|
189
189
|
try:
|
190
|
-
with open(
|
190
|
+
with style_path.open() as f:
|
191
191
|
content = f.read()
|
192
192
|
if "Claude MPM Output Style" in content:
|
193
193
|
return DiagnosticResult(
|
@@ -232,7 +232,7 @@ class ClaudeCodeCheck(BaseDiagnosticCheck):
|
|
232
232
|
)
|
233
233
|
|
234
234
|
try:
|
235
|
-
with open(
|
235
|
+
with config_path.open() as f:
|
236
236
|
config = json.load(f)
|
237
237
|
|
238
238
|
mcp_servers = config.get("mcpServers", {})
|
@@ -114,7 +114,7 @@ class CommonIssuesCheck(BaseDiagnosticCheck):
|
|
114
114
|
# Try to count conversations
|
115
115
|
conversation_count = 0
|
116
116
|
try:
|
117
|
-
with open(
|
117
|
+
with claude_json_path.open() as f:
|
118
118
|
data = json.load(f)
|
119
119
|
if isinstance(data, dict) and "conversations" in data:
|
120
120
|
conversation_count = len(data["conversations"])
|
@@ -283,10 +283,10 @@ class CommonIssuesCheck(BaseDiagnosticCheck):
|
|
283
283
|
try:
|
284
284
|
import yaml
|
285
285
|
|
286
|
-
with open(
|
286
|
+
with user_config.open() as f:
|
287
287
|
user_data = yaml.safe_load(f) or {}
|
288
288
|
|
289
|
-
with open(
|
289
|
+
with project_config.open() as f:
|
290
290
|
project_data = yaml.safe_load(f) or {}
|
291
291
|
|
292
292
|
# Check for conflicting keys
|
@@ -91,7 +91,7 @@ class ConfigurationCheck(BaseDiagnosticCheck):
|
|
91
91
|
)
|
92
92
|
|
93
93
|
try:
|
94
|
-
with open(
|
94
|
+
with config_path.open() as f:
|
95
95
|
config = yaml.safe_load(f)
|
96
96
|
|
97
97
|
issues = self._validate_config_structure(config)
|
@@ -143,7 +143,7 @@ class ConfigurationCheck(BaseDiagnosticCheck):
|
|
143
143
|
)
|
144
144
|
|
145
145
|
try:
|
146
|
-
with open(
|
146
|
+
with config_path.open() as f:
|
147
147
|
config = yaml.safe_load(f)
|
148
148
|
|
149
149
|
issues = self._validate_config_structure(config)
|
@@ -138,7 +138,7 @@ class InstallationCheck(BaseDiagnosticCheck):
|
|
138
138
|
details={"error": str(e)},
|
139
139
|
)
|
140
140
|
|
141
|
-
def _check_installation_method(self) -> DiagnosticResult:
|
141
|
+
def _check_installation_method(self) -> DiagnosticResult:
|
142
142
|
"""Detect how claude-mpm was installed."""
|
143
143
|
methods_found = []
|
144
144
|
details = {}
|
@@ -369,7 +369,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
369
369
|
stderr_output = stderr_data.decode(
|
370
370
|
"utf-8", errors="ignore"
|
371
371
|
)[:200]
|
372
|
-
except:
|
372
|
+
except (asyncio.TimeoutError, OSError):
|
373
373
|
pass
|
374
374
|
|
375
375
|
if stderr_output:
|
@@ -391,7 +391,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
391
391
|
stderr_output = stderr_data.decode(
|
392
392
|
"utf-8", errors="ignore"
|
393
393
|
)[:200]
|
394
|
-
except:
|
394
|
+
except (asyncio.TimeoutError, OSError):
|
395
395
|
pass
|
396
396
|
|
397
397
|
if stderr_output:
|
@@ -778,7 +778,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
778
778
|
return None
|
779
779
|
|
780
780
|
try:
|
781
|
-
with open(
|
781
|
+
with claude_config_path.open() as f:
|
782
782
|
config = json.load(f)
|
783
783
|
|
784
784
|
mcp_servers = config.get("mcpServers", {})
|
@@ -869,7 +869,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
869
869
|
|
870
870
|
# Create backup
|
871
871
|
backup_path = config_path.with_suffix(".json.backup")
|
872
|
-
with open(
|
872
|
+
with backup_path.open("w") as f:
|
873
873
|
json.dump(config, f, indent=2)
|
874
874
|
|
875
875
|
# Update the configuration - ensure we're setting the exact new_args
|
@@ -884,11 +884,11 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
884
884
|
return False
|
885
885
|
|
886
886
|
# Write updated configuration
|
887
|
-
with open(
|
887
|
+
with config_path.open("w") as f:
|
888
888
|
json.dump(config, f, indent=2)
|
889
889
|
|
890
890
|
# Verify the file was written correctly
|
891
|
-
with open(
|
891
|
+
with config_path.open() as f:
|
892
892
|
verify_config = json.load(f)
|
893
893
|
verify_args = (
|
894
894
|
verify_config.get("mcpServers", {})
|
@@ -902,9 +902,9 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
902
902
|
f"Expected {new_args}, got {verify_args}"
|
903
903
|
)
|
904
904
|
# Restore backup
|
905
|
-
with open(
|
905
|
+
with backup_path.open() as bf:
|
906
906
|
backup_config = json.load(bf)
|
907
|
-
with open(
|
907
|
+
with config_path.open("w") as f:
|
908
908
|
json.dump(backup_config, f, indent=2)
|
909
909
|
return False
|
910
910
|
|
@@ -936,7 +936,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
|
|
936
936
|
fix_description="Initialize Claude configuration",
|
937
937
|
)
|
938
938
|
|
939
|
-
with open(
|
939
|
+
with config_file.open() as f:
|
940
940
|
config = json.load(f)
|
941
941
|
|
942
942
|
# Get the current project configuration
|
@@ -193,7 +193,7 @@ class MonitorCheck(BaseDiagnosticCheck):
|
|
193
193
|
for config_path in config_paths:
|
194
194
|
if config_path.exists():
|
195
195
|
try:
|
196
|
-
with open(
|
196
|
+
with config_path.open() as f:
|
197
197
|
config = yaml.safe_load(f)
|
198
198
|
if config and isinstance(config, dict):
|
199
199
|
response_config = config.get("response_logging", {})
|
@@ -248,7 +248,7 @@ class DoctorReporter:
|
|
248
248
|
# Header with timestamp
|
249
249
|
print("# Claude MPM Doctor Report")
|
250
250
|
print(
|
251
|
-
f"\n**Generated:** {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
251
|
+
f"\n**Generated:** {datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M:%S')}"
|
252
252
|
)
|
253
253
|
print(f"**Version:** {self._get_version()}\n")
|
254
254
|
print("---\n")
|
@@ -65,6 +65,9 @@ class EventBus:
|
|
65
65
|
self._event_history: List[Dict[str, Any]] = []
|
66
66
|
self._max_history_size = 100
|
67
67
|
|
68
|
+
# Track async handler tasks to prevent garbage collection
|
69
|
+
self._handler_tasks: Set[asyncio.Task] = set()
|
70
|
+
|
68
71
|
logger.info("EventBus initialized")
|
69
72
|
|
70
73
|
@classmethod
|
@@ -190,13 +193,17 @@ class EventBus:
|
|
190
193
|
try:
|
191
194
|
# Call with event_type and data for wildcard handlers
|
192
195
|
if asyncio.iscoroutinefunction(handler):
|
193
|
-
# Schedule async handlers
|
196
|
+
# Schedule async handlers with tracking
|
194
197
|
try:
|
195
198
|
loop = asyncio.get_event_loop()
|
196
199
|
if loop.is_running():
|
197
|
-
asyncio.create_task(
|
200
|
+
task = asyncio.create_task(
|
198
201
|
handler(event_type, data)
|
199
202
|
)
|
203
|
+
self._handler_tasks.add(task)
|
204
|
+
task.add_done_callback(
|
205
|
+
self._handler_tasks.discard
|
206
|
+
)
|
200
207
|
else:
|
201
208
|
loop.run_until_complete(
|
202
209
|
handler(event_type, data)
|
@@ -109,7 +109,7 @@ class DeadLetterConsumer(IEventConsumer):
|
|
109
109
|
self._rotate_file()
|
110
110
|
|
111
111
|
# Write to file
|
112
|
-
with
|
112
|
+
with self._current_file.open("a") as f:
|
113
113
|
f.write(event_json)
|
114
114
|
|
115
115
|
# Update metrics
|
@@ -165,7 +165,7 @@ class DeadLetterConsumer(IEventConsumer):
|
|
165
165
|
# Find files in time range
|
166
166
|
for file_path in sorted(self.output_dir.glob("dead-letter-*.jsonl")):
|
167
167
|
try:
|
168
|
-
with open(
|
168
|
+
with file_path.open() as f:
|
169
169
|
for line in f:
|
170
170
|
event_data = json.loads(line)
|
171
171
|
|