claude-mpm 4.3.11__py3-none-any.whl → 4.3.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/PM_INSTRUCTIONS.md +390 -28
- claude_mpm/agents/templates/data_engineer.json +39 -14
- claude_mpm/agents/templates/research.json +20 -8
- claude_mpm/agents/templates/web_qa.json +25 -10
- claude_mpm/cli/__init__.py +1 -0
- claude_mpm/cli/commands/agent_manager.py +3 -3
- claude_mpm/cli/commands/agents.py +2 -2
- claude_mpm/cli/commands/aggregate.py +1 -1
- claude_mpm/cli/commands/config.py +2 -2
- claude_mpm/cli/commands/configure.py +5 -5
- claude_mpm/cli/commands/configure_tui.py +7 -7
- claude_mpm/cli/commands/dashboard.py +1 -1
- claude_mpm/cli/commands/debug.py +5 -5
- claude_mpm/cli/commands/mcp.py +1 -1
- claude_mpm/cli/commands/mcp_command_router.py +12 -1
- claude_mpm/cli/commands/mcp_config.py +154 -0
- claude_mpm/cli/commands/mcp_external_commands.py +249 -0
- claude_mpm/cli/commands/mcp_install_commands.py +93 -24
- claude_mpm/cli/commands/mcp_setup_external.py +870 -0
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init_handler.py +1 -1
- claude_mpm/cli/commands/run.py +114 -0
- claude_mpm/cli/commands/search.py +292 -0
- claude_mpm/cli/interactive/agent_wizard.py +2 -2
- claude_mpm/cli/parsers/base_parser.py +13 -0
- claude_mpm/cli/parsers/mcp_parser.py +15 -0
- claude_mpm/cli/parsers/run_parser.py +5 -0
- claude_mpm/cli/parsers/search_parser.py +245 -0
- claude_mpm/cli/startup_logging.py +3 -5
- claude_mpm/cli/utils.py +1 -1
- claude_mpm/constants.py +1 -0
- claude_mpm/core/agent_registry.py +12 -8
- claude_mpm/core/agent_session_manager.py +8 -8
- claude_mpm/core/api_validator.py +4 -4
- claude_mpm/core/base_service.py +10 -10
- claude_mpm/core/cache.py +5 -5
- claude_mpm/core/config_constants.py +1 -1
- claude_mpm/core/container.py +1 -1
- claude_mpm/core/error_handler.py +2 -2
- claude_mpm/core/file_utils.py +1 -1
- claude_mpm/core/framework_loader.py +3 -3
- claude_mpm/core/hook_manager.py +8 -6
- claude_mpm/core/instruction_reinforcement_hook.py +2 -2
- claude_mpm/core/interactive_session.py +1 -1
- claude_mpm/core/lazy.py +3 -3
- claude_mpm/core/log_manager.py +16 -12
- claude_mpm/core/logger.py +16 -11
- claude_mpm/core/logging_config.py +4 -2
- claude_mpm/core/oneshot_session.py +1 -1
- claude_mpm/core/optimized_agent_loader.py +6 -6
- claude_mpm/core/output_style_manager.py +1 -1
- claude_mpm/core/pm_hook_interceptor.py +3 -3
- claude_mpm/core/service_registry.py +1 -1
- claude_mpm/core/session_manager.py +11 -9
- claude_mpm/core/socketio_pool.py +13 -13
- claude_mpm/core/types.py +2 -2
- claude_mpm/core/unified_agent_registry.py +9 -2
- claude_mpm/core/unified_paths.py +1 -1
- claude_mpm/dashboard/analysis_runner.py +4 -4
- claude_mpm/dashboard/api/simple_directory.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +4 -2
- claude_mpm/hooks/base_hook.py +2 -2
- claude_mpm/hooks/claude_hooks/connection_pool.py +4 -4
- claude_mpm/hooks/claude_hooks/event_handlers.py +12 -12
- claude_mpm/hooks/claude_hooks/hook_handler.py +4 -4
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +3 -3
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +15 -14
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +4 -4
- claude_mpm/hooks/claude_hooks/installer.py +3 -3
- claude_mpm/hooks/claude_hooks/memory_integration.py +3 -3
- claude_mpm/hooks/claude_hooks/response_tracking.py +3 -3
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +5 -5
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +3 -3
- claude_mpm/hooks/claude_hooks/services/state_manager.py +8 -7
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +3 -3
- claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
- claude_mpm/hooks/memory_integration_hook.py +1 -1
- claude_mpm/hooks/tool_call_interceptor.py +2 -2
- claude_mpm/models/agent_session.py +5 -5
- claude_mpm/services/__init__.py +1 -1
- claude_mpm/services/agent_capabilities_service.py +1 -1
- claude_mpm/services/agents/agent_builder.py +3 -3
- claude_mpm/services/agents/deployment/agent_deployment.py +29 -13
- claude_mpm/services/agents/deployment/agent_discovery_service.py +22 -6
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +7 -5
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +3 -1
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
- claude_mpm/services/agents/deployment/agent_operation_service.py +2 -2
- claude_mpm/services/agents/deployment/agent_state_service.py +2 -2
- claude_mpm/services/agents/deployment/agent_template_builder.py +1 -1
- claude_mpm/services/agents/deployment/agent_versioning.py +1 -1
- claude_mpm/services/agents/deployment/deployment_wrapper.py +2 -3
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +6 -4
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +1 -1
- claude_mpm/services/agents/loading/agent_profile_loader.py +5 -3
- claude_mpm/services/agents/loading/base_agent_manager.py +2 -2
- claude_mpm/services/agents/local_template_manager.py +6 -6
- claude_mpm/services/agents/management/agent_management_service.py +3 -3
- claude_mpm/services/agents/memory/content_manager.py +3 -3
- claude_mpm/services/agents/memory/memory_format_service.py +2 -2
- claude_mpm/services/agents/memory/template_generator.py +3 -3
- claude_mpm/services/agents/registry/__init__.py +1 -1
- claude_mpm/services/agents/registry/modification_tracker.py +2 -2
- claude_mpm/services/async_session_logger.py +3 -3
- claude_mpm/services/claude_session_logger.py +4 -4
- claude_mpm/services/cli/agent_cleanup_service.py +5 -0
- claude_mpm/services/cli/agent_listing_service.py +1 -1
- claude_mpm/services/cli/agent_validation_service.py +1 -0
- claude_mpm/services/cli/memory_crud_service.py +11 -6
- claude_mpm/services/cli/memory_output_formatter.py +1 -1
- claude_mpm/services/cli/session_manager.py +15 -11
- claude_mpm/services/cli/unified_dashboard_manager.py +1 -1
- claude_mpm/services/core/memory_manager.py +81 -23
- claude_mpm/services/core/path_resolver.py +2 -2
- claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
- claude_mpm/services/event_aggregator.py +4 -2
- claude_mpm/services/event_bus/direct_relay.py +5 -3
- claude_mpm/services/event_bus/event_bus.py +3 -3
- claude_mpm/services/event_bus/relay.py +6 -4
- claude_mpm/services/events/consumers/dead_letter.py +5 -3
- claude_mpm/services/events/core.py +3 -3
- claude_mpm/services/events/producers/hook.py +6 -6
- claude_mpm/services/events/producers/system.py +8 -8
- claude_mpm/services/exceptions.py +5 -5
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +2 -2
- claude_mpm/services/hook_installer_service.py +1 -1
- claude_mpm/services/infrastructure/context_preservation.py +6 -4
- claude_mpm/services/infrastructure/daemon_manager.py +2 -2
- claude_mpm/services/infrastructure/logging.py +2 -2
- claude_mpm/services/mcp_config_manager.py +439 -0
- claude_mpm/services/mcp_gateway/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/auto_configure.py +3 -3
- claude_mpm/services/mcp_gateway/config/config_loader.py +1 -1
- claude_mpm/services/mcp_gateway/config/configuration.py +18 -1
- claude_mpm/services/mcp_gateway/core/base.py +2 -2
- claude_mpm/services/mcp_gateway/main.py +52 -0
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +10 -8
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +4 -4
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
- claude_mpm/services/mcp_gateway/server/stdio_server.py +4 -3
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +15 -15
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +7 -5
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +443 -0
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +5 -5
- claude_mpm/services/mcp_gateway/tools/hello_world.py +9 -9
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +16 -16
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +17 -17
- claude_mpm/services/memory/builder.py +7 -5
- claude_mpm/services/memory/indexed_memory.py +4 -4
- claude_mpm/services/memory/optimizer.py +6 -6
- claude_mpm/services/memory/router.py +3 -3
- claude_mpm/services/monitor/daemon.py +1 -1
- claude_mpm/services/monitor/daemon_manager.py +6 -6
- claude_mpm/services/monitor/event_emitter.py +2 -2
- claude_mpm/services/monitor/handlers/file.py +1 -1
- claude_mpm/services/monitor/management/lifecycle.py +1 -1
- claude_mpm/services/monitor/server.py +4 -4
- claude_mpm/services/monitor_build_service.py +2 -2
- claude_mpm/services/port_manager.py +2 -2
- claude_mpm/services/response_tracker.py +2 -2
- claude_mpm/services/session_management_service.py +3 -2
- claude_mpm/services/socketio/client_proxy.py +2 -2
- claude_mpm/services/socketio/dashboard_server.py +4 -3
- claude_mpm/services/socketio/event_normalizer.py +12 -8
- claude_mpm/services/socketio/handlers/base.py +2 -2
- claude_mpm/services/socketio/handlers/connection.py +10 -10
- claude_mpm/services/socketio/handlers/connection_handler.py +13 -10
- claude_mpm/services/socketio/handlers/file.py +1 -1
- claude_mpm/services/socketio/handlers/git.py +1 -1
- claude_mpm/services/socketio/handlers/hook.py +16 -15
- claude_mpm/services/socketio/migration_utils.py +1 -1
- claude_mpm/services/socketio/monitor_client.py +5 -5
- claude_mpm/services/socketio/server/broadcaster.py +9 -7
- claude_mpm/services/socketio/server/connection_manager.py +2 -2
- claude_mpm/services/socketio/server/core.py +7 -5
- claude_mpm/services/socketio/server/eventbus_integration.py +18 -11
- claude_mpm/services/socketio/server/main.py +13 -13
- claude_mpm/services/socketio_client_manager.py +4 -4
- claude_mpm/services/system_instructions_service.py +2 -2
- claude_mpm/services/ticket_services/validation_service.py +1 -1
- claude_mpm/services/utility_service.py +5 -2
- claude_mpm/services/version_control/branch_strategy.py +2 -2
- claude_mpm/services/version_control/git_operations.py +22 -20
- claude_mpm/services/version_control/semantic_versioning.py +3 -3
- claude_mpm/services/version_control/version_parser.py +7 -5
- claude_mpm/services/visualization/mermaid_generator.py +1 -1
- claude_mpm/storage/state_storage.py +1 -1
- claude_mpm/tools/code_tree_analyzer.py +19 -18
- claude_mpm/tools/code_tree_builder.py +2 -2
- claude_mpm/tools/code_tree_events.py +10 -8
- claude_mpm/tools/socketio_debug.py +3 -3
- claude_mpm/utils/agent_dependency_loader.py +2 -2
- claude_mpm/utils/dependency_strategies.py +8 -3
- claude_mpm/utils/environment_context.py +2 -2
- claude_mpm/utils/error_handler.py +2 -2
- claude_mpm/utils/file_utils.py +1 -1
- claude_mpm/utils/imports.py +1 -1
- claude_mpm/utils/log_cleanup.py +21 -7
- claude_mpm/validation/agent_validator.py +2 -2
- {claude_mpm-4.3.11.dist-info → claude_mpm-4.3.13.dist-info}/METADATA +4 -1
- {claude_mpm-4.3.11.dist-info → claude_mpm-4.3.13.dist-info}/RECORD +207 -200
- {claude_mpm-4.3.11.dist-info → claude_mpm-4.3.13.dist-info}/WHEEL +0 -0
- {claude_mpm-4.3.11.dist-info → claude_mpm-4.3.13.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.3.11.dist-info → claude_mpm-4.3.13.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.3.11.dist-info → claude_mpm-4.3.13.dist-info}/top_level.txt +0 -0
|
@@ -30,7 +30,7 @@ import shutil
|
|
|
30
30
|
import time
|
|
31
31
|
import uuid
|
|
32
32
|
from dataclasses import asdict, dataclass, field
|
|
33
|
-
from datetime import datetime
|
|
33
|
+
from datetime import datetime, timezone
|
|
34
34
|
from enum import Enum
|
|
35
35
|
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
|
|
36
36
|
|
|
@@ -90,7 +90,7 @@ class AgentModification:
|
|
|
90
90
|
@property
|
|
91
91
|
def modification_datetime(self) -> datetime:
|
|
92
92
|
"""Get modification timestamp as datetime."""
|
|
93
|
-
return datetime.fromtimestamp(self.timestamp)
|
|
93
|
+
return datetime.fromtimestamp(self.timestamp, tz=timezone.utc)
|
|
94
94
|
|
|
95
95
|
@property
|
|
96
96
|
def age_seconds(self) -> float:
|
|
@@ -22,7 +22,7 @@ import os
|
|
|
22
22
|
import sys
|
|
23
23
|
import time
|
|
24
24
|
from dataclasses import asdict, dataclass
|
|
25
|
-
from datetime import datetime
|
|
25
|
+
from datetime import datetime, timezone
|
|
26
26
|
from enum import Enum
|
|
27
27
|
from queue import Full, Queue
|
|
28
28
|
from threading import Lock, Thread
|
|
@@ -184,7 +184,7 @@ class AsyncSessionLogger:
|
|
|
184
184
|
return session_id
|
|
185
185
|
|
|
186
186
|
# Generate timestamp-based session ID
|
|
187
|
-
session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
187
|
+
session_id = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
188
188
|
logger.info(f"Generated session ID: {session_id}")
|
|
189
189
|
return session_id
|
|
190
190
|
|
|
@@ -406,7 +406,7 @@ class AsyncSessionLogger:
|
|
|
406
406
|
agent_name = metadata["agent"].replace(" ", "_").lower()
|
|
407
407
|
|
|
408
408
|
# Create timestamp with microsecond precision
|
|
409
|
-
now = datetime.now()
|
|
409
|
+
now = datetime.now(timezone.utc)
|
|
410
410
|
timestamp = now.isoformat()
|
|
411
411
|
microseconds = now.microsecond
|
|
412
412
|
|
|
@@ -13,7 +13,7 @@ Configuration via .claude-mpm/configuration.yaml.
|
|
|
13
13
|
import json
|
|
14
14
|
import logging
|
|
15
15
|
import os
|
|
16
|
-
from datetime import datetime
|
|
16
|
+
from datetime import datetime, timezone
|
|
17
17
|
from typing import Any, Dict, Optional
|
|
18
18
|
|
|
19
19
|
# Import configuration manager
|
|
@@ -133,7 +133,7 @@ class ClaudeSessionLogger:
|
|
|
133
133
|
# Generate a default based on timestamp if nothing found
|
|
134
134
|
if not session_id:
|
|
135
135
|
# Use a timestamp-based session ID as fallback
|
|
136
|
-
session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
136
|
+
session_id = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
137
137
|
logger.info(f"No Claude session ID found, using generated: {session_id}")
|
|
138
138
|
else:
|
|
139
139
|
logger.info(f"Using Claude session ID: {session_id}")
|
|
@@ -156,7 +156,7 @@ class ClaudeSessionLogger:
|
|
|
156
156
|
agent_name = agent_name.replace(" ", "_").lower()
|
|
157
157
|
|
|
158
158
|
# Generate timestamp with microseconds for uniqueness
|
|
159
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
|
159
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S_%f")
|
|
160
160
|
|
|
161
161
|
# Create filename: session_id-agent-timestamp.json
|
|
162
162
|
return f"{self.session_id}-{agent_name}-{timestamp}.json"
|
|
@@ -216,7 +216,7 @@ class ClaudeSessionLogger:
|
|
|
216
216
|
|
|
217
217
|
# Prepare response data with standardized field names
|
|
218
218
|
response_data = {
|
|
219
|
-
"timestamp": datetime.now().isoformat(),
|
|
219
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
220
220
|
"session_id": self.session_id,
|
|
221
221
|
"request": request_summary, # Standardized field name
|
|
222
222
|
"response": response_content, # Already correct
|
|
@@ -138,6 +138,11 @@ class AgentCleanupService(IAgentCleanupService):
|
|
|
138
138
|
if not isinstance(result, dict):
|
|
139
139
|
result = {"success": bool(result)}
|
|
140
140
|
|
|
141
|
+
# Add success flag based on whether there were errors
|
|
142
|
+
if "success" not in result:
|
|
143
|
+
# Consider it successful if no errors occurred
|
|
144
|
+
result["success"] = not bool(result.get("errors"))
|
|
145
|
+
|
|
141
146
|
# Add cleaned_count for backward compatibility
|
|
142
147
|
if "cleaned_count" not in result:
|
|
143
148
|
removed_count = len(result.get("removed", []))
|
|
@@ -336,7 +336,7 @@ class AgentListingService(IAgentListingService):
|
|
|
336
336
|
self.logger.error(f"Error listing agents by tier: {e}", exc_info=True)
|
|
337
337
|
return AgentTierInfo(project=[], user=[], system=[])
|
|
338
338
|
|
|
339
|
-
def get_agent_details(self, agent_name: str) -> Optional[Dict[str, Any]]:
|
|
339
|
+
def get_agent_details(self, agent_name: str) -> Optional[Dict[str, Any]]: # noqa: PLR0911
|
|
340
340
|
"""Get detailed information for a specific agent."""
|
|
341
341
|
cache_key = f"agent_details_{agent_name}"
|
|
342
342
|
cached = self._get_from_cache(cache_key)
|
|
@@ -17,7 +17,7 @@ DESIGN DECISIONS:
|
|
|
17
17
|
|
|
18
18
|
import os
|
|
19
19
|
from abc import ABC, abstractmethod
|
|
20
|
-
from datetime import datetime
|
|
20
|
+
from datetime import datetime, timezone
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
from typing import Any, Dict, List, Optional
|
|
23
23
|
|
|
@@ -223,7 +223,9 @@ class MemoryCRUDService(IMemoryCRUDService):
|
|
|
223
223
|
stat = memory_file.stat()
|
|
224
224
|
file_stats = {
|
|
225
225
|
"size_kb": stat.st_size / 1024,
|
|
226
|
-
"modified": datetime.fromtimestamp(
|
|
226
|
+
"modified": datetime.fromtimestamp(
|
|
227
|
+
stat.st_mtime, tz=timezone.utc
|
|
228
|
+
).isoformat(),
|
|
227
229
|
"path": str(memory_file),
|
|
228
230
|
}
|
|
229
231
|
|
|
@@ -256,7 +258,7 @@ class MemoryCRUDService(IMemoryCRUDService):
|
|
|
256
258
|
"file_stats": {
|
|
257
259
|
"size_kb": stat.st_size / 1024,
|
|
258
260
|
"modified": datetime.fromtimestamp(
|
|
259
|
-
stat.st_mtime
|
|
261
|
+
stat.st_mtime, tz=timezone.utc
|
|
260
262
|
).isoformat(),
|
|
261
263
|
"path": str(memory_file),
|
|
262
264
|
},
|
|
@@ -431,10 +433,10 @@ class MemoryCRUDService(IMemoryCRUDService):
|
|
|
431
433
|
{
|
|
432
434
|
"size_kb": stat.st_size / 1024,
|
|
433
435
|
"modified": datetime.fromtimestamp(
|
|
434
|
-
stat.st_mtime
|
|
436
|
+
stat.st_mtime, tz=timezone.utc
|
|
435
437
|
).isoformat(),
|
|
436
438
|
"created": datetime.fromtimestamp(
|
|
437
|
-
stat.st_ctime
|
|
439
|
+
stat.st_ctime, tz=timezone.utc
|
|
438
440
|
).isoformat(),
|
|
439
441
|
}
|
|
440
442
|
)
|
|
@@ -499,7 +501,10 @@ class MemoryCRUDService(IMemoryCRUDService):
|
|
|
499
501
|
continue
|
|
500
502
|
|
|
501
503
|
stat = memory_file.stat()
|
|
502
|
-
age_days = (
|
|
504
|
+
age_days = (
|
|
505
|
+
datetime.now(timezone.utc)
|
|
506
|
+
- datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc)
|
|
507
|
+
).days
|
|
503
508
|
|
|
504
509
|
# Identify files older than 30 days as candidates
|
|
505
510
|
if age_days > 30:
|
|
@@ -200,7 +200,7 @@ class MemoryOutputFormatter(IMemoryOutputFormatter):
|
|
|
200
200
|
try:
|
|
201
201
|
dt = datetime.fromisoformat(last_modified.replace("Z", "+00:00"))
|
|
202
202
|
last_modified_str = dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
203
|
-
except:
|
|
203
|
+
except Exception:
|
|
204
204
|
last_modified_str = last_modified
|
|
205
205
|
|
|
206
206
|
# Status indicator based on usage
|
|
@@ -18,7 +18,7 @@ import json
|
|
|
18
18
|
import uuid
|
|
19
19
|
from abc import ABC, abstractmethod
|
|
20
20
|
from dataclasses import dataclass, field
|
|
21
|
-
from datetime import datetime, timedelta
|
|
21
|
+
from datetime import datetime, timedelta, timezone
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
from typing import Any, Dict, List, Optional
|
|
24
24
|
|
|
@@ -151,8 +151,12 @@ class SessionInfo:
|
|
|
151
151
|
|
|
152
152
|
id: str
|
|
153
153
|
context: str = "default"
|
|
154
|
-
created_at: str = field(
|
|
155
|
-
|
|
154
|
+
created_at: str = field(
|
|
155
|
+
default_factory=lambda: datetime.now(timezone.utc).isoformat()
|
|
156
|
+
)
|
|
157
|
+
last_used: str = field(
|
|
158
|
+
default_factory=lambda: datetime.now(timezone.utc).isoformat()
|
|
159
|
+
)
|
|
156
160
|
use_count: int = 0
|
|
157
161
|
agents_run: List[Dict[str, Any]] = field(default_factory=list)
|
|
158
162
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
@@ -175,8 +179,8 @@ class SessionInfo:
|
|
|
175
179
|
return cls(
|
|
176
180
|
id=data["id"],
|
|
177
181
|
context=data.get("context", "default"),
|
|
178
|
-
created_at=data.get("created_at", datetime.now().isoformat()),
|
|
179
|
-
last_used=data.get("last_used", datetime.now().isoformat()),
|
|
182
|
+
created_at=data.get("created_at", datetime.now(timezone.utc).isoformat()),
|
|
183
|
+
last_used=data.get("last_used", datetime.now(timezone.utc).isoformat()),
|
|
180
184
|
use_count=data.get("use_count", 0),
|
|
181
185
|
agents_run=data.get("agents_run", []),
|
|
182
186
|
metadata=data.get("metadata", {}),
|
|
@@ -284,7 +288,7 @@ class SessionManager(ISessionManager):
|
|
|
284
288
|
# Check session age
|
|
285
289
|
try:
|
|
286
290
|
created = datetime.fromisoformat(session.created_at)
|
|
287
|
-
age = datetime.now() - created
|
|
291
|
+
age = datetime.now(timezone.utc) - created
|
|
288
292
|
|
|
289
293
|
if age > timedelta(days=7):
|
|
290
294
|
validation.warnings.append(f"Session is {age.days} days old")
|
|
@@ -355,10 +359,10 @@ class SessionManager(ISessionManager):
|
|
|
355
359
|
{
|
|
356
360
|
"agent": agent,
|
|
357
361
|
"task": task[:100], # Truncate long tasks
|
|
358
|
-
"timestamp": datetime.now().isoformat(),
|
|
362
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
359
363
|
}
|
|
360
364
|
)
|
|
361
|
-
session.last_used = datetime.now().isoformat()
|
|
365
|
+
session.last_used = datetime.now(timezone.utc).isoformat()
|
|
362
366
|
session.use_count += 1
|
|
363
367
|
|
|
364
368
|
self.save_session(session)
|
|
@@ -370,7 +374,7 @@ class SessionManager(ISessionManager):
|
|
|
370
374
|
|
|
371
375
|
WHY: Prevents unbounded growth of session data and improves performance.
|
|
372
376
|
"""
|
|
373
|
-
now = datetime.now()
|
|
377
|
+
now = datetime.now(timezone.utc)
|
|
374
378
|
max_age = timedelta(hours=max_age_hours)
|
|
375
379
|
|
|
376
380
|
expired_ids = []
|
|
@@ -418,7 +422,7 @@ class SessionManager(ISessionManager):
|
|
|
418
422
|
return True
|
|
419
423
|
|
|
420
424
|
# Create timestamped archive file
|
|
421
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
425
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
422
426
|
archive_name = f"sessions_archive_{timestamp}.json.gz"
|
|
423
427
|
archive_path = archive_dir / archive_name
|
|
424
428
|
|
|
@@ -509,5 +513,5 @@ class ManagedSession:
|
|
|
509
513
|
"""Exit session context with cleanup."""
|
|
510
514
|
if self.session:
|
|
511
515
|
# Update last used time
|
|
512
|
-
self.session.last_used = datetime.now().isoformat()
|
|
516
|
+
self.session.last_used = datetime.now(timezone.utc).isoformat()
|
|
513
517
|
self.manager.save_session(self.session)
|
|
@@ -81,7 +81,7 @@ class UnifiedDashboardManager(IUnifiedDashboardManager):
|
|
|
81
81
|
background: bool = False,
|
|
82
82
|
open_browser: bool = True,
|
|
83
83
|
force_restart: bool = False,
|
|
84
|
-
) -> Tuple[bool, bool]:
|
|
84
|
+
) -> Tuple[bool, bool]: # noqa: PLR0911
|
|
85
85
|
"""
|
|
86
86
|
Start the dashboard using unified daemon.
|
|
87
87
|
|
|
@@ -21,7 +21,7 @@ ARCHITECTURE:
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
import logging
|
|
24
|
-
from datetime import datetime
|
|
24
|
+
from datetime import datetime, timezone
|
|
25
25
|
from pathlib import Path
|
|
26
26
|
from typing import Any, Dict, List, Optional, Set
|
|
27
27
|
|
|
@@ -169,7 +169,7 @@ class MemoryManager(IMemoryManager):
|
|
|
169
169
|
]
|
|
170
170
|
|
|
171
171
|
# Add new memory as a bullet point
|
|
172
|
-
timestamp = datetime.now().isoformat()
|
|
172
|
+
timestamp = datetime.now(timezone.utc).isoformat()
|
|
173
173
|
lines.append(f"- [{timestamp}] {key}: {value}")
|
|
174
174
|
|
|
175
175
|
# Write back
|
|
@@ -335,19 +335,37 @@ class MemoryManager(IMemoryManager):
|
|
|
335
335
|
if pm_memories:
|
|
336
336
|
aggregated_pm = self._aggregate_memories(pm_memories)
|
|
337
337
|
result["actual_memories"] = aggregated_pm
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
338
|
+
# Count actual memory items in aggregated content
|
|
339
|
+
memory_items = [
|
|
340
|
+
line.strip()
|
|
341
|
+
for line in aggregated_pm.split("\n")
|
|
342
|
+
if line.strip().startswith("-")
|
|
343
|
+
]
|
|
344
|
+
if memory_items:
|
|
345
|
+
self.logger.info(
|
|
346
|
+
f"Aggregated PM memory: {len(memory_items)} total items from {len(pm_memories)} source(s)"
|
|
347
|
+
)
|
|
348
|
+
else:
|
|
349
|
+
self.logger.debug(
|
|
350
|
+
f"Aggregated PM memory from {len(pm_memories)} source(s) (no items)"
|
|
351
|
+
)
|
|
342
352
|
|
|
343
353
|
# Store agent memories (already aggregated per agent)
|
|
344
354
|
if agent_memories_dict:
|
|
345
355
|
result["agent_memories"] = agent_memories_dict
|
|
346
356
|
for agent_name, memory_content in agent_memories_dict.items():
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
357
|
+
# Count actual memory items
|
|
358
|
+
memory_items = [
|
|
359
|
+
line.strip()
|
|
360
|
+
for line in memory_content.split("\n")
|
|
361
|
+
if line.strip().startswith("-")
|
|
362
|
+
]
|
|
363
|
+
if memory_items:
|
|
364
|
+
self.logger.debug(
|
|
365
|
+
f"Aggregated {agent_name} memory: {len(memory_items)} items"
|
|
366
|
+
)
|
|
367
|
+
else:
|
|
368
|
+
self.logger.debug(f"Aggregated {agent_name} memory: no items")
|
|
351
369
|
|
|
352
370
|
# Log summary
|
|
353
371
|
if self._stats["loaded_count"] > 0 or self._stats["skipped_count"] > 0:
|
|
@@ -410,10 +428,21 @@ class MemoryManager(IMemoryManager):
|
|
|
410
428
|
"path": pm_memory_path,
|
|
411
429
|
}
|
|
412
430
|
)
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
431
|
+
# Count actual memory items (lines starting with "-")
|
|
432
|
+
memory_items = [
|
|
433
|
+
line.strip()
|
|
434
|
+
for line in loaded_content.split("\n")
|
|
435
|
+
if line.strip().startswith("-")
|
|
436
|
+
]
|
|
437
|
+
if memory_items:
|
|
438
|
+
self.logger.info(
|
|
439
|
+
f"Loaded {source} PM memory: {len(memory_items)} items"
|
|
440
|
+
)
|
|
441
|
+
else:
|
|
442
|
+
# Skip logging if no actual memory items
|
|
443
|
+
self.logger.debug(
|
|
444
|
+
f"Skipped {source} PM memory: {pm_memory_path} (no memory items)"
|
|
445
|
+
)
|
|
417
446
|
self._stats["loaded_count"] += 1
|
|
418
447
|
except Exception as e:
|
|
419
448
|
self.logger.error(
|
|
@@ -471,20 +500,47 @@ class MemoryManager(IMemoryManager):
|
|
|
471
500
|
}
|
|
472
501
|
)
|
|
473
502
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
503
|
+
# Count actual memory items (lines starting with "-")
|
|
504
|
+
memory_items = [
|
|
505
|
+
line.strip()
|
|
506
|
+
for line in loaded_content.split("\n")
|
|
507
|
+
if line.strip().startswith("-")
|
|
508
|
+
]
|
|
509
|
+
if memory_items:
|
|
510
|
+
self.logger.info(
|
|
511
|
+
f"Loaded {source} memory for {agent_name}: {len(memory_items)} items"
|
|
512
|
+
)
|
|
513
|
+
else:
|
|
514
|
+
# Skip logging if no actual memory items
|
|
515
|
+
self.logger.debug(
|
|
516
|
+
f"Skipped {source} memory for {agent_name}: {memory_file.name} (no memory items)"
|
|
517
|
+
)
|
|
478
518
|
self._stats["loaded_count"] += 1
|
|
479
519
|
except Exception as e:
|
|
480
520
|
self.logger.error(
|
|
481
521
|
f"Failed to load agent memory from {memory_file}: {e}"
|
|
482
522
|
)
|
|
483
523
|
else:
|
|
484
|
-
# Log skipped memories
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
524
|
+
# Log skipped memories only if they contain actual items
|
|
525
|
+
try:
|
|
526
|
+
loaded_content = memory_file.read_text(encoding="utf-8")
|
|
527
|
+
memory_items = [
|
|
528
|
+
line.strip()
|
|
529
|
+
for line in loaded_content.split("\n")
|
|
530
|
+
if line.strip().startswith("-")
|
|
531
|
+
]
|
|
532
|
+
if memory_items:
|
|
533
|
+
self.logger.info(
|
|
534
|
+
f"Skipped {source} memory: {memory_file.name} (agent '{agent_name}' not deployed, {len(memory_items)} items)"
|
|
535
|
+
)
|
|
536
|
+
else:
|
|
537
|
+
# Don't log if file has no actual memory items
|
|
538
|
+
self.logger.debug(
|
|
539
|
+
f"Skipped {source} memory: {memory_file.name} (agent '{agent_name}' not deployed, no items)"
|
|
540
|
+
)
|
|
541
|
+
except Exception:
|
|
542
|
+
# If we can't read the file, just skip silently
|
|
543
|
+
pass
|
|
488
544
|
|
|
489
545
|
# Detect naming mismatches
|
|
490
546
|
alt_name = (
|
|
@@ -570,7 +626,9 @@ class MemoryManager(IMemoryManager):
|
|
|
570
626
|
lines.append("# Agent Memory")
|
|
571
627
|
|
|
572
628
|
# Add latest timestamp
|
|
573
|
-
lines.append(
|
|
629
|
+
lines.append(
|
|
630
|
+
f"<!-- Last Updated: {datetime.now(timezone.utc).isoformat()}Z -->"
|
|
631
|
+
)
|
|
574
632
|
lines.append("")
|
|
575
633
|
|
|
576
634
|
# Add all unique items (sorted for consistency)
|
|
@@ -163,7 +163,7 @@ class PathResolver(IPathResolver):
|
|
|
163
163
|
self.logger.debug(f"No project root found from {start_path}")
|
|
164
164
|
return None
|
|
165
165
|
|
|
166
|
-
def detect_framework_path(self) -> Optional[Path]:
|
|
166
|
+
def detect_framework_path(self) -> Optional[Path]: # noqa: PLR0911
|
|
167
167
|
"""
|
|
168
168
|
Auto-detect claude-mpm framework using unified path management.
|
|
169
169
|
|
|
@@ -322,8 +322,8 @@ class PathResolver(IPathResolver):
|
|
|
322
322
|
"""Detect framework path using unified path management."""
|
|
323
323
|
try:
|
|
324
324
|
# Import here to avoid circular dependencies
|
|
325
|
+
from ...core.unified_paths import DeploymentContext as UnifiedContext
|
|
325
326
|
from ...core.unified_paths import (
|
|
326
|
-
DeploymentContext as UnifiedContext,
|
|
327
327
|
get_path_manager,
|
|
328
328
|
)
|
|
329
329
|
|
|
@@ -136,7 +136,7 @@ class InstallationCheck(BaseDiagnosticCheck):
|
|
|
136
136
|
details={"error": str(e)},
|
|
137
137
|
)
|
|
138
138
|
|
|
139
|
-
def _check_installation_method(self) -> DiagnosticResult:
|
|
139
|
+
def _check_installation_method(self) -> DiagnosticResult: # noqa: PLR0911
|
|
140
140
|
"""Detect how claude-mpm was installed."""
|
|
141
141
|
methods_found = []
|
|
142
142
|
details = {}
|
|
@@ -18,7 +18,7 @@ import sys
|
|
|
18
18
|
import threading
|
|
19
19
|
import time
|
|
20
20
|
from collections import defaultdict
|
|
21
|
-
from datetime import datetime
|
|
21
|
+
from datetime import datetime, timezone
|
|
22
22
|
from typing import Any, Dict, List, Optional
|
|
23
23
|
|
|
24
24
|
try:
|
|
@@ -267,7 +267,9 @@ class EventAggregator:
|
|
|
267
267
|
try:
|
|
268
268
|
# Extract event metadata
|
|
269
269
|
event_type = event_data.get("type", "unknown")
|
|
270
|
-
timestamp = event_data.get(
|
|
270
|
+
timestamp = event_data.get(
|
|
271
|
+
"timestamp", datetime.now(timezone.utc).isoformat() + "Z"
|
|
272
|
+
)
|
|
271
273
|
data = event_data.get("data", {})
|
|
272
274
|
|
|
273
275
|
# Update statistics
|
|
@@ -15,7 +15,7 @@ DO NOT use "event" or "type" fields - use "hook_event_name" instead!
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
import logging
|
|
18
|
-
from datetime import datetime
|
|
18
|
+
from datetime import datetime, timezone
|
|
19
19
|
from typing import Any
|
|
20
20
|
|
|
21
21
|
from .event_bus import EventBus
|
|
@@ -223,7 +223,9 @@ class DirectSocketIORelay:
|
|
|
223
223
|
event_type, broadcast_data
|
|
224
224
|
)
|
|
225
225
|
self.stats["events_relayed"] += 1
|
|
226
|
-
self.stats["last_relay_time"] = datetime.now(
|
|
226
|
+
self.stats["last_relay_time"] = datetime.now(
|
|
227
|
+
timezone.utc
|
|
228
|
+
).isoformat()
|
|
227
229
|
|
|
228
230
|
# Reset retry counter on successful broadcast
|
|
229
231
|
if self.connection_retries > 0:
|
|
@@ -258,7 +260,7 @@ class DirectSocketIORelay:
|
|
|
258
260
|
logger.info(
|
|
259
261
|
f"[DirectRelay] Retry successful for {event_type}"
|
|
260
262
|
)
|
|
261
|
-
except:
|
|
263
|
+
except Exception:
|
|
262
264
|
pass # Already counted as failed
|
|
263
265
|
else:
|
|
264
266
|
# Enhanced logging when broadcaster is not available
|
|
@@ -11,7 +11,7 @@ WHY pyee over alternatives:
|
|
|
11
11
|
import asyncio
|
|
12
12
|
import logging
|
|
13
13
|
import threading
|
|
14
|
-
from datetime import datetime
|
|
14
|
+
from datetime import datetime, timezone
|
|
15
15
|
from typing import Any, Callable, Dict, List, Optional, Set
|
|
16
16
|
|
|
17
17
|
from pyee.asyncio import AsyncIOEventEmitter
|
|
@@ -210,7 +210,7 @@ class EventBus:
|
|
|
210
210
|
|
|
211
211
|
# Update stats
|
|
212
212
|
self._stats["events_published"] += 1
|
|
213
|
-
self._stats["last_event_time"] = datetime.now().isoformat()
|
|
213
|
+
self._stats["last_event_time"] = datetime.now(timezone.utc).isoformat()
|
|
214
214
|
|
|
215
215
|
if self._debug:
|
|
216
216
|
logger.debug(f"Published event: {event_type}")
|
|
@@ -302,7 +302,7 @@ class EventBus:
|
|
|
302
302
|
data: The event data
|
|
303
303
|
"""
|
|
304
304
|
event_record = {
|
|
305
|
-
"timestamp": datetime.now().isoformat(),
|
|
305
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
306
306
|
"type": event_type,
|
|
307
307
|
"data": data,
|
|
308
308
|
}
|
|
@@ -11,7 +11,7 @@ WHY separate relay component:
|
|
|
11
11
|
import logging
|
|
12
12
|
import os
|
|
13
13
|
import time
|
|
14
|
-
from datetime import datetime
|
|
14
|
+
from datetime import datetime, timezone
|
|
15
15
|
from typing import Any, Dict, Optional
|
|
16
16
|
|
|
17
17
|
# Socket.IO imports
|
|
@@ -151,7 +151,7 @@ class SocketIORelay:
|
|
|
151
151
|
# Verify connection is still alive
|
|
152
152
|
if self.client.connected:
|
|
153
153
|
return True
|
|
154
|
-
except:
|
|
154
|
+
except Exception:
|
|
155
155
|
pass
|
|
156
156
|
|
|
157
157
|
# Need to create or reconnect
|
|
@@ -187,7 +187,9 @@ class SocketIORelay:
|
|
|
187
187
|
"subtype": (
|
|
188
188
|
event_type.split(".", 1)[1] if "." in event_type else "generic"
|
|
189
189
|
),
|
|
190
|
-
"timestamp": data.get(
|
|
190
|
+
"timestamp": data.get(
|
|
191
|
+
"timestamp", datetime.now(timezone.utc).isoformat()
|
|
192
|
+
),
|
|
191
193
|
"data": data,
|
|
192
194
|
"source": "event_bus",
|
|
193
195
|
},
|
|
@@ -195,7 +197,7 @@ class SocketIORelay:
|
|
|
195
197
|
|
|
196
198
|
# Update statistics
|
|
197
199
|
self.stats["events_relayed"] += 1
|
|
198
|
-
self.stats["last_relay_time"] = datetime.now().isoformat()
|
|
200
|
+
self.stats["last_relay_time"] = datetime.now(timezone.utc).isoformat()
|
|
199
201
|
|
|
200
202
|
if self.debug:
|
|
201
203
|
logger.debug(f"Relayed event to Socket.IO: {event_type}")
|
|
@@ -6,7 +6,7 @@ Handles events that failed processing in other consumers.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
|
-
from datetime import datetime
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from typing import Any, Dict, List, Optional
|
|
12
12
|
|
|
@@ -284,7 +284,7 @@ class DeadLetterConsumer(IEventConsumer):
|
|
|
284
284
|
|
|
285
285
|
def _rotate_file(self) -> None:
|
|
286
286
|
"""Rotate to a new output file."""
|
|
287
|
-
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
287
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
|
|
288
288
|
self._current_file = self.output_dir / f"dead-letter-{timestamp}.jsonl"
|
|
289
289
|
self._current_file_size = 0
|
|
290
290
|
self._metrics["files_created"] += 1
|
|
@@ -293,7 +293,9 @@ class DeadLetterConsumer(IEventConsumer):
|
|
|
293
293
|
|
|
294
294
|
async def _cleanup_old_files(self) -> None:
|
|
295
295
|
"""Remove files older than retention period."""
|
|
296
|
-
cutoff_time = datetime.now().timestamp() - (
|
|
296
|
+
cutoff_time = datetime.now(timezone.utc).timestamp() - (
|
|
297
|
+
self.retention_days * 86400
|
|
298
|
+
)
|
|
297
299
|
|
|
298
300
|
for file_path in self.output_dir.glob("dead-letter-*.jsonl"):
|
|
299
301
|
try:
|
|
@@ -12,7 +12,7 @@ import time
|
|
|
12
12
|
import uuid
|
|
13
13
|
from collections import defaultdict, deque
|
|
14
14
|
from dataclasses import dataclass, field
|
|
15
|
-
from datetime import datetime
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
16
|
from enum import Enum
|
|
17
17
|
from typing import Any, Deque, Dict, List, Optional, Set
|
|
18
18
|
|
|
@@ -225,7 +225,7 @@ class EventBus(IEventBus):
|
|
|
225
225
|
|
|
226
226
|
# Add metadata
|
|
227
227
|
if event.metadata:
|
|
228
|
-
event.metadata.published_at = datetime.now()
|
|
228
|
+
event.metadata.published_at = datetime.now(timezone.utc)
|
|
229
229
|
|
|
230
230
|
# Queue event
|
|
231
231
|
self._event_queues[event.priority].append(event)
|
|
@@ -353,7 +353,7 @@ class EventBus(IEventBus):
|
|
|
353
353
|
# Update metrics
|
|
354
354
|
if events_processed > 0:
|
|
355
355
|
self._metrics["events_processed"] += events_processed
|
|
356
|
-
self._metrics["last_event_time"] = datetime.now()
|
|
356
|
+
self._metrics["last_event_time"] = datetime.now(timezone.utc)
|
|
357
357
|
self._metrics["queue_size"] = sum(
|
|
358
358
|
len(q) for q in self._event_queues.values()
|
|
359
359
|
)
|