claude-mpm 4.3.12__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/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 +1 -1
- claude_mpm/cli/commands/mcp_config.py +7 -10
- claude_mpm/cli/commands/mcp_external_commands.py +40 -32
- claude_mpm/cli/commands/mcp_install_commands.py +38 -10
- claude_mpm/cli/commands/mcp_setup_external.py +143 -102
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init_handler.py +1 -1
- claude_mpm/cli/commands/run.py +46 -2
- claude_mpm/cli/commands/search.py +41 -34
- claude_mpm/cli/interactive/agent_wizard.py +2 -2
- claude_mpm/cli/parsers/mcp_parser.py +1 -3
- claude_mpm/cli/parsers/search_parser.py +10 -4
- claude_mpm/cli/startup_logging.py +3 -5
- claude_mpm/cli/utils.py +1 -1
- 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 +2 -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 +2 -1
- claude_mpm/services/agents/deployment/agent_discovery_service.py +9 -3
- 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/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_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 +175 -30
- 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 +1 -1
- claude_mpm/services/mcp_gateway/core/base.py +2 -2
- claude_mpm/services/mcp_gateway/main.py +21 -7
- 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 +190 -137
- 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.12.dist-info → claude_mpm-4.3.13.dist-info}/METADATA +1 -1
- {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/RECORD +199 -199
- {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/WHEEL +0 -0
- {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/top_level.txt +0 -0
claude_mpm/core/hook_manager.py
CHANGED
|
@@ -19,7 +19,7 @@ import queue
|
|
|
19
19
|
import subprocess
|
|
20
20
|
import threading
|
|
21
21
|
import uuid
|
|
22
|
-
from datetime import datetime
|
|
22
|
+
from datetime import datetime, timezone
|
|
23
23
|
from typing import Any, Dict, Optional
|
|
24
24
|
|
|
25
25
|
from ..core.logger import get_logger
|
|
@@ -106,7 +106,9 @@ class HookManager:
|
|
|
106
106
|
hook_event = {
|
|
107
107
|
"hook_event_name": hook_type,
|
|
108
108
|
"session_id": self.session_id,
|
|
109
|
-
"timestamp": hook_data.get(
|
|
109
|
+
"timestamp": hook_data.get(
|
|
110
|
+
"timestamp", datetime.now(timezone.utc).isoformat()
|
|
111
|
+
),
|
|
110
112
|
**event_data,
|
|
111
113
|
}
|
|
112
114
|
|
|
@@ -182,7 +184,7 @@ class HookManager:
|
|
|
182
184
|
{
|
|
183
185
|
"tool_name": tool_name,
|
|
184
186
|
"tool_args": tool_args or {},
|
|
185
|
-
"timestamp": datetime.
|
|
187
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
186
188
|
},
|
|
187
189
|
)
|
|
188
190
|
|
|
@@ -205,7 +207,7 @@ class HookManager:
|
|
|
205
207
|
"tool_name": tool_name,
|
|
206
208
|
"exit_code": exit_code,
|
|
207
209
|
"result": str(result) if result is not None else None,
|
|
208
|
-
"timestamp": datetime.
|
|
210
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
209
211
|
},
|
|
210
212
|
)
|
|
211
213
|
|
|
@@ -220,7 +222,7 @@ class HookManager:
|
|
|
220
222
|
"""
|
|
221
223
|
return self._trigger_hook_event(
|
|
222
224
|
"UserPromptSubmit",
|
|
223
|
-
{"prompt": prompt, "timestamp": datetime.
|
|
225
|
+
{"prompt": prompt, "timestamp": datetime.now(timezone.utc).isoformat()},
|
|
224
226
|
)
|
|
225
227
|
|
|
226
228
|
def _trigger_hook_event(self, hook_type: str, event_data: Dict[str, Any]) -> bool:
|
|
@@ -250,7 +252,7 @@ class HookManager:
|
|
|
250
252
|
hook_data = {
|
|
251
253
|
"hook_type": hook_type,
|
|
252
254
|
"event_data": event_data,
|
|
253
|
-
"timestamp": datetime.
|
|
255
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
254
256
|
}
|
|
255
257
|
|
|
256
258
|
# Try to queue without blocking
|
|
@@ -18,7 +18,7 @@ The hook works by:
|
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
import threading
|
|
21
|
-
from datetime import datetime
|
|
21
|
+
from datetime import datetime, timezone
|
|
22
22
|
from typing import Any, Dict, List, Optional
|
|
23
23
|
|
|
24
24
|
from ..core.logger import get_logger
|
|
@@ -197,7 +197,7 @@ class InstructionReinforcementHook:
|
|
|
197
197
|
"enabled": self.enabled,
|
|
198
198
|
"test_mode": self.test_mode,
|
|
199
199
|
"injection_interval": self.injection_interval,
|
|
200
|
-
"timestamp": datetime.
|
|
200
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
def reset_counters(self):
|
|
@@ -154,7 +154,7 @@ class InteractiveSession:
|
|
|
154
154
|
self.logger.error(error_msg)
|
|
155
155
|
return False, {}
|
|
156
156
|
|
|
157
|
-
def handle_interactive_input(self, environment: Dict[str, Any]) -> bool:
|
|
157
|
+
def handle_interactive_input(self, environment: Dict[str, Any]) -> bool: # noqa: PLR0911
|
|
158
158
|
"""Handle the interactive input/output loop.
|
|
159
159
|
|
|
160
160
|
Launches Claude and manages the interactive session using either
|
claude_mpm/core/lazy.py
CHANGED
|
@@ -16,7 +16,7 @@ import functools
|
|
|
16
16
|
import threading
|
|
17
17
|
import time
|
|
18
18
|
from dataclasses import dataclass, field
|
|
19
|
-
from datetime import datetime
|
|
19
|
+
from datetime import datetime, timezone
|
|
20
20
|
from typing import Any, Callable, Dict, Generic, Optional, Type, TypeVar
|
|
21
21
|
|
|
22
22
|
from ..core.logger import get_logger
|
|
@@ -121,7 +121,7 @@ class LazyService(Generic[T]):
|
|
|
121
121
|
# Track initialization
|
|
122
122
|
start_time = time.time()
|
|
123
123
|
if self._metrics.first_access is None:
|
|
124
|
-
self._metrics.first_access = datetime.now()
|
|
124
|
+
self._metrics.first_access = datetime.now(timezone.utc)
|
|
125
125
|
|
|
126
126
|
try:
|
|
127
127
|
self._logger.debug(f"Initializing lazy service: {self._name}")
|
|
@@ -417,7 +417,7 @@ class AsyncLazyService(Generic[T]):
|
|
|
417
417
|
|
|
418
418
|
start_time = time.time()
|
|
419
419
|
if self._metrics.first_access is None:
|
|
420
|
-
self._metrics.first_access = datetime.now()
|
|
420
|
+
self._metrics.first_access = datetime.now(timezone.utc)
|
|
421
421
|
|
|
422
422
|
try:
|
|
423
423
|
self._logger.debug(f"Async initializing: {self._name}")
|
claude_mpm/core/log_manager.py
CHANGED
|
@@ -18,7 +18,7 @@ import asyncio
|
|
|
18
18
|
import json
|
|
19
19
|
import logging
|
|
20
20
|
import os
|
|
21
|
-
from datetime import datetime, timedelta
|
|
21
|
+
from datetime import datetime, timedelta, timezone
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
from queue import Full, Queue
|
|
24
24
|
from threading import Lock, Thread
|
|
@@ -191,7 +191,7 @@ class LogManager:
|
|
|
191
191
|
finally:
|
|
192
192
|
self.write_queue.task_done()
|
|
193
193
|
|
|
194
|
-
except:
|
|
194
|
+
except Exception:
|
|
195
195
|
continue # Timeout or other error, continue loop
|
|
196
196
|
|
|
197
197
|
def _process_cleanup_queue(self):
|
|
@@ -211,7 +211,7 @@ class LogManager:
|
|
|
211
211
|
finally:
|
|
212
212
|
self.cleanup_queue.task_done()
|
|
213
213
|
|
|
214
|
-
except:
|
|
214
|
+
except Exception:
|
|
215
215
|
continue # Timeout or other error, continue loop
|
|
216
216
|
|
|
217
217
|
async def setup_logging(self, log_type: str) -> Path:
|
|
@@ -296,7 +296,7 @@ class LogManager:
|
|
|
296
296
|
return 0
|
|
297
297
|
|
|
298
298
|
# Calculate cutoff time
|
|
299
|
-
cutoff_time = datetime.now() - timedelta(hours=retention_hours)
|
|
299
|
+
cutoff_time = datetime.now(timezone.utc) - timedelta(hours=retention_hours)
|
|
300
300
|
|
|
301
301
|
# Schedule async cleanup
|
|
302
302
|
deleted_count = await self._async_cleanup(directory, pattern, cutoff_time)
|
|
@@ -337,7 +337,9 @@ class LogManager:
|
|
|
337
337
|
|
|
338
338
|
try:
|
|
339
339
|
# Check file modification time
|
|
340
|
-
mtime = datetime.fromtimestamp(
|
|
340
|
+
mtime = datetime.fromtimestamp(
|
|
341
|
+
file_path.stat().st_mtime, tz=timezone.utc
|
|
342
|
+
)
|
|
341
343
|
if mtime < cutoff_time:
|
|
342
344
|
file_path.unlink()
|
|
343
345
|
deleted_count += 1
|
|
@@ -370,7 +372,7 @@ class LogManager:
|
|
|
370
372
|
return 0
|
|
371
373
|
|
|
372
374
|
# Calculate cutoff time
|
|
373
|
-
cutoff_time = datetime.now() - timedelta(hours=retention_hours)
|
|
375
|
+
cutoff_time = datetime.now(timezone.utc) - timedelta(hours=retention_hours)
|
|
374
376
|
deleted_count = 0
|
|
375
377
|
|
|
376
378
|
try:
|
|
@@ -386,7 +388,9 @@ class LogManager:
|
|
|
386
388
|
|
|
387
389
|
try:
|
|
388
390
|
# Check file modification time
|
|
389
|
-
mtime = datetime.fromtimestamp(
|
|
391
|
+
mtime = datetime.fromtimestamp(
|
|
392
|
+
file_path.stat().st_mtime, tz=timezone.utc
|
|
393
|
+
)
|
|
390
394
|
if mtime < cutoff_time:
|
|
391
395
|
file_path.unlink()
|
|
392
396
|
deleted_count += 1
|
|
@@ -468,7 +472,7 @@ class LogManager:
|
|
|
468
472
|
prompts_dir = await self.setup_logging("prompts")
|
|
469
473
|
|
|
470
474
|
# Generate filename with timestamp
|
|
471
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")[
|
|
475
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S_%f")[
|
|
472
476
|
:-3
|
|
473
477
|
] # Microseconds to milliseconds
|
|
474
478
|
|
|
@@ -494,7 +498,7 @@ class LogManager:
|
|
|
494
498
|
|
|
495
499
|
# Prepare prompt data
|
|
496
500
|
prompt_data = {
|
|
497
|
-
"timestamp": datetime.now().isoformat(),
|
|
501
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
498
502
|
"type": prompt_type,
|
|
499
503
|
"content": content,
|
|
500
504
|
"metadata": metadata or {},
|
|
@@ -567,12 +571,12 @@ class LogManager:
|
|
|
567
571
|
message: Log message to write
|
|
568
572
|
level: Log level (INFO, WARNING, ERROR, DEBUG)
|
|
569
573
|
"""
|
|
570
|
-
timestamp = datetime.now().isoformat()
|
|
574
|
+
timestamp = datetime.now(timezone.utc).isoformat()
|
|
571
575
|
log_entry = f"[{timestamp}] [{level}] {message}\n"
|
|
572
576
|
|
|
573
577
|
# Get appropriate log file based on context
|
|
574
578
|
log_dir = self._get_log_directory("mpm")
|
|
575
|
-
log_file = log_dir / f"mpm_{datetime.now().strftime('%Y%m%d')}.log"
|
|
579
|
+
log_file = log_dir / f"mpm_{datetime.now(timezone.utc).strftime('%Y%m%d')}.log"
|
|
576
580
|
|
|
577
581
|
def write_task():
|
|
578
582
|
try:
|
|
@@ -652,7 +656,7 @@ class LogManager:
|
|
|
652
656
|
try:
|
|
653
657
|
self.write_queue.put_nowait(None)
|
|
654
658
|
self.cleanup_queue.put_nowait(None)
|
|
655
|
-
except:
|
|
659
|
+
except Exception:
|
|
656
660
|
pass
|
|
657
661
|
|
|
658
662
|
# Wait for threads to finish
|
claude_mpm/core/logger.py
CHANGED
|
@@ -14,7 +14,7 @@ import sys
|
|
|
14
14
|
import threading
|
|
15
15
|
import time
|
|
16
16
|
from collections import defaultdict
|
|
17
|
-
from datetime import datetime
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
18
|
from enum import Enum
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
from typing import Any, Dict, Optional
|
|
@@ -84,7 +84,7 @@ class StreamingHandler(logging.StreamHandler):
|
|
|
84
84
|
|
|
85
85
|
except (KeyboardInterrupt, SystemExit):
|
|
86
86
|
raise
|
|
87
|
-
except:
|
|
87
|
+
except Exception:
|
|
88
88
|
self.handleError(record)
|
|
89
89
|
|
|
90
90
|
def finalize_info_line(self):
|
|
@@ -237,7 +237,7 @@ def setup_logging(
|
|
|
237
237
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
238
238
|
|
|
239
239
|
# Create timestamped log file
|
|
240
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
240
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
241
241
|
log_file = log_dir / f"mpm_{timestamp}.log"
|
|
242
242
|
|
|
243
243
|
file_handler = logging.FileHandler(log_file)
|
|
@@ -485,8 +485,8 @@ class ProjectLogger:
|
|
|
485
485
|
path.mkdir(parents=True, exist_ok=True)
|
|
486
486
|
|
|
487
487
|
# Create session directory
|
|
488
|
-
self.session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
489
|
-
self.session_start_time = datetime.now()
|
|
488
|
+
self.session_id = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
489
|
+
self.session_start_time = datetime.now(timezone.utc)
|
|
490
490
|
self.session_dir = self.dirs["logs_sessions"] / self.session_id
|
|
491
491
|
self.session_dir.mkdir(parents=True, exist_ok=True)
|
|
492
492
|
|
|
@@ -513,7 +513,7 @@ class ProjectLogger:
|
|
|
513
513
|
if self.log_level == LogLevel.OFF:
|
|
514
514
|
return
|
|
515
515
|
|
|
516
|
-
timestamp = datetime.now().isoformat()
|
|
516
|
+
timestamp = datetime.now(timezone.utc).isoformat()
|
|
517
517
|
log_entry = {
|
|
518
518
|
"timestamp": timestamp,
|
|
519
519
|
"level": level,
|
|
@@ -523,7 +523,8 @@ class ProjectLogger:
|
|
|
523
523
|
|
|
524
524
|
# Write to daily log file
|
|
525
525
|
log_file = (
|
|
526
|
-
self.dirs["logs_system"]
|
|
526
|
+
self.dirs["logs_system"]
|
|
527
|
+
/ f"{datetime.now(timezone.utc).strftime('%Y%m%d')}.jsonl"
|
|
527
528
|
)
|
|
528
529
|
with open(log_file, "a") as f:
|
|
529
530
|
f.write(json.dumps(log_entry) + "\n")
|
|
@@ -543,10 +544,10 @@ class ProjectLogger:
|
|
|
543
544
|
if self.log_level == LogLevel.OFF:
|
|
544
545
|
return
|
|
545
546
|
|
|
546
|
-
timestamp = datetime.now().isoformat()
|
|
547
|
+
timestamp = datetime.now(timezone.utc).isoformat()
|
|
547
548
|
|
|
548
549
|
# Update statistics
|
|
549
|
-
today = datetime.now().strftime("%Y-%m-%d")
|
|
550
|
+
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
|
550
551
|
self.stats[today]["total_calls"] += 1
|
|
551
552
|
self.stats[today]["total_tokens"] += tokens
|
|
552
553
|
self.stats[today]["total_time_seconds"] += execution_time
|
|
@@ -576,7 +577,9 @@ class ProjectLogger:
|
|
|
576
577
|
agent_log_dir = self.dirs["logs_agents"] / agent.lower()
|
|
577
578
|
agent_log_dir.mkdir(exist_ok=True)
|
|
578
579
|
|
|
579
|
-
daily_log =
|
|
580
|
+
daily_log = (
|
|
581
|
+
agent_log_dir / f"{datetime.now(timezone.utc).strftime('%Y%m%d')}.jsonl"
|
|
582
|
+
)
|
|
580
583
|
with open(daily_log, "a") as f:
|
|
581
584
|
f.write(json.dumps(log_entry) + "\n")
|
|
582
585
|
|
|
@@ -586,7 +589,9 @@ class ProjectLogger:
|
|
|
586
589
|
"session_id": self.session_id,
|
|
587
590
|
"session_dir": str(self.session_dir),
|
|
588
591
|
"start_time": self.session_id,
|
|
589
|
-
"stats": self.stats.get(
|
|
592
|
+
"stats": self.stats.get(
|
|
593
|
+
datetime.now(timezone.utc).strftime("%Y-%m-%d"), {}
|
|
594
|
+
),
|
|
590
595
|
}
|
|
591
596
|
|
|
592
597
|
|
|
@@ -27,11 +27,13 @@ from typing import Any, Dict, Optional, Union
|
|
|
27
27
|
from claude_mpm.core.logger import (
|
|
28
28
|
JsonFormatter,
|
|
29
29
|
finalize_streaming_logs,
|
|
30
|
-
|
|
30
|
+
)
|
|
31
|
+
from claude_mpm.core.logger import get_logger as _get_logger
|
|
32
|
+
from claude_mpm.core.logger import (
|
|
31
33
|
log_async_performance,
|
|
32
34
|
log_performance,
|
|
33
|
-
setup_logging as _setup_logging,
|
|
34
35
|
)
|
|
36
|
+
from claude_mpm.core.logger import setup_logging as _setup_logging
|
|
35
37
|
|
|
36
38
|
# Standard log format for consistency
|
|
37
39
|
STANDARD_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
@@ -166,7 +166,7 @@ class OneshotSession:
|
|
|
166
166
|
|
|
167
167
|
def _run_subprocess(
|
|
168
168
|
self, cmd: list, env: dict, prompt: str
|
|
169
|
-
) -> Tuple[bool, Optional[str]]:
|
|
169
|
+
) -> Tuple[bool, Optional[str]]: # noqa: PLR0911
|
|
170
170
|
"""Run the subprocess and handle all exception types."""
|
|
171
171
|
try:
|
|
172
172
|
# Debug: log the command being run
|
|
@@ -22,7 +22,7 @@ import threading
|
|
|
22
22
|
import time
|
|
23
23
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
24
24
|
from dataclasses import dataclass
|
|
25
|
-
from datetime import datetime
|
|
25
|
+
from datetime import datetime, timezone
|
|
26
26
|
from typing import Any, Dict, List, Optional
|
|
27
27
|
|
|
28
28
|
try:
|
|
@@ -111,7 +111,7 @@ class OptimizedAgentLoader:
|
|
|
111
111
|
try:
|
|
112
112
|
mtime = file_path.stat().st_mtime
|
|
113
113
|
return f"agent:{file_path}:{mtime}"
|
|
114
|
-
except:
|
|
114
|
+
except Exception:
|
|
115
115
|
return f"agent:{file_path}"
|
|
116
116
|
|
|
117
117
|
def _load_agent_file(self, file_path: Path) -> Optional[Dict[str, Any]]:
|
|
@@ -154,7 +154,7 @@ class OptimizedAgentLoader:
|
|
|
154
154
|
|
|
155
155
|
# Add metadata
|
|
156
156
|
agent_data["_file_path"] = str(file_path)
|
|
157
|
-
agent_data["_loaded_at"] = datetime.now().isoformat()
|
|
157
|
+
agent_data["_loaded_at"] = datetime.now(timezone.utc).isoformat()
|
|
158
158
|
|
|
159
159
|
# Cache the result
|
|
160
160
|
self.cache.put(cache_key, agent_data, ttl=self.cache_ttl)
|
|
@@ -188,7 +188,7 @@ class OptimizedAgentLoader:
|
|
|
188
188
|
data = yaml.safe_load(frontmatter) or {}
|
|
189
189
|
data["instructions"] = body
|
|
190
190
|
return data
|
|
191
|
-
except:
|
|
191
|
+
except Exception:
|
|
192
192
|
pass
|
|
193
193
|
|
|
194
194
|
# No frontmatter, treat as pure instructions
|
|
@@ -322,7 +322,7 @@ class OptimizedAgentLoader:
|
|
|
322
322
|
|
|
323
323
|
# Add metadata
|
|
324
324
|
agent_data["_file_path"] = str(file_path)
|
|
325
|
-
agent_data["_loaded_at"] = datetime.now().isoformat()
|
|
325
|
+
agent_data["_loaded_at"] = datetime.now(timezone.utc).isoformat()
|
|
326
326
|
|
|
327
327
|
# Cache the result
|
|
328
328
|
self.cache.put(cache_key, agent_data, ttl=self.cache_ttl)
|
|
@@ -366,7 +366,7 @@ class OptimizedAgentLoader:
|
|
|
366
366
|
"template": template,
|
|
367
367
|
"variables": self._extract_variables(template),
|
|
368
368
|
"sections": self._extract_sections(template),
|
|
369
|
-
"compiled_at": datetime.now().isoformat(),
|
|
369
|
+
"compiled_at": datetime.now(timezone.utc).isoformat(),
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
# Cache compiled template
|
|
@@ -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]: # noqa: PLR0911
|
|
46
46
|
"""
|
|
47
47
|
Detect Claude Code version by running 'claude --version'.
|
|
48
48
|
Uses global cache to avoid duplicate detection and logging.
|
|
@@ -14,7 +14,7 @@ WHY this is needed:
|
|
|
14
14
|
import functools
|
|
15
15
|
import threading
|
|
16
16
|
import time
|
|
17
|
-
from datetime import datetime
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
18
|
from typing import Any, Dict, List, Optional
|
|
19
19
|
|
|
20
20
|
from ..core.hook_manager import get_hook_manager
|
|
@@ -195,7 +195,7 @@ class PMHookInterceptor:
|
|
|
195
195
|
{
|
|
196
196
|
"todos": todos,
|
|
197
197
|
"source": "PM_Manual",
|
|
198
|
-
"timestamp": datetime.
|
|
198
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
199
199
|
},
|
|
200
200
|
)
|
|
201
201
|
|
|
@@ -210,7 +210,7 @@ class PMHookInterceptor:
|
|
|
210
210
|
"todos_count": len(todos),
|
|
211
211
|
"source": "PM_Manual",
|
|
212
212
|
"success": True,
|
|
213
|
-
"timestamp": datetime.
|
|
213
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
214
214
|
},
|
|
215
215
|
)
|
|
216
216
|
|
|
@@ -160,7 +160,7 @@ 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:
|
|
163
|
+
except Exception:
|
|
164
164
|
# Fall back to looking up class and resolving
|
|
165
165
|
if service_type not in self._services:
|
|
166
166
|
raise KeyError(f"Service '{service_type}' not registered")
|
|
@@ -4,7 +4,7 @@ import gzip
|
|
|
4
4
|
import json
|
|
5
5
|
import shutil
|
|
6
6
|
import uuid
|
|
7
|
-
from datetime import datetime, timedelta
|
|
7
|
+
from datetime import datetime, timedelta, timezone
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any, Dict, List, Optional
|
|
10
10
|
|
|
@@ -41,8 +41,8 @@ class SessionManager:
|
|
|
41
41
|
self.active_sessions[session_id] = {
|
|
42
42
|
"id": session_id,
|
|
43
43
|
"context": context,
|
|
44
|
-
"created_at": datetime.now().isoformat(),
|
|
45
|
-
"last_used": datetime.now().isoformat(),
|
|
44
|
+
"created_at": datetime.now(timezone.utc).isoformat(),
|
|
45
|
+
"last_used": datetime.now(timezone.utc).isoformat(),
|
|
46
46
|
"use_count": 0,
|
|
47
47
|
"agents_run": [],
|
|
48
48
|
}
|
|
@@ -65,7 +65,7 @@ class SessionManager:
|
|
|
65
65
|
Session ID
|
|
66
66
|
"""
|
|
67
67
|
# Look for existing session in context
|
|
68
|
-
now = datetime.now()
|
|
68
|
+
now = datetime.now(timezone.utc)
|
|
69
69
|
max_age = timedelta(minutes=max_age_minutes)
|
|
70
70
|
|
|
71
71
|
for session_id, session_data in self.active_sessions.items():
|
|
@@ -95,10 +95,12 @@ class SessionManager:
|
|
|
95
95
|
{
|
|
96
96
|
"agent": agent,
|
|
97
97
|
"task": task[:100], # Truncate long tasks
|
|
98
|
-
"timestamp": datetime.now().isoformat(),
|
|
98
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
99
99
|
}
|
|
100
100
|
)
|
|
101
|
-
self.active_sessions[session_id]["last_used"] = datetime.now(
|
|
101
|
+
self.active_sessions[session_id]["last_used"] = datetime.now(
|
|
102
|
+
timezone.utc
|
|
103
|
+
).isoformat()
|
|
102
104
|
self._save_sessions()
|
|
103
105
|
|
|
104
106
|
def cleanup_old_sessions(self, max_age_hours: int = 24, archive: bool = True):
|
|
@@ -111,7 +113,7 @@ class SessionManager:
|
|
|
111
113
|
max_age_hours: Maximum age in hours
|
|
112
114
|
archive: Whether to archive sessions before removing
|
|
113
115
|
"""
|
|
114
|
-
now = datetime.now()
|
|
116
|
+
now = datetime.now(timezone.utc)
|
|
115
117
|
max_age = timedelta(hours=max_age_hours)
|
|
116
118
|
|
|
117
119
|
expired = []
|
|
@@ -223,7 +225,7 @@ class SessionManager:
|
|
|
223
225
|
archive_dir.mkdir(parents=True, exist_ok=True)
|
|
224
226
|
|
|
225
227
|
# Create timestamped archive file
|
|
226
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
228
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
227
229
|
archive_name = f"sessions_archive_{timestamp}.json.gz"
|
|
228
230
|
archive_path = archive_dir / archive_name
|
|
229
231
|
|
|
@@ -279,7 +281,7 @@ class SessionManager:
|
|
|
279
281
|
archive_dir = Path.home() / ".claude-mpm" / "archives"
|
|
280
282
|
archive_dir.mkdir(parents=True, exist_ok=True)
|
|
281
283
|
|
|
282
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
284
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
283
285
|
backup_name = f"claude_json_backup_{timestamp}.json.gz"
|
|
284
286
|
backup_path = archive_dir / backup_name
|
|
285
287
|
|
claude_mpm/core/socketio_pool.py
CHANGED
|
@@ -17,7 +17,7 @@ import threading
|
|
|
17
17
|
import time
|
|
18
18
|
from collections import defaultdict, deque
|
|
19
19
|
from dataclasses import dataclass, field
|
|
20
|
-
from datetime import datetime, timedelta
|
|
20
|
+
from datetime import datetime, timedelta, timezone
|
|
21
21
|
from enum import Enum
|
|
22
22
|
from typing import Any, Deque, Dict, List, Optional
|
|
23
23
|
|
|
@@ -97,11 +97,9 @@ class CircuitBreaker:
|
|
|
97
97
|
return True
|
|
98
98
|
if self.state == CircuitState.OPEN:
|
|
99
99
|
# Check if recovery timeout has passed
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
> timedelta(seconds=self.recovery_timeout)
|
|
104
|
-
):
|
|
100
|
+
if self.last_failure_time and datetime.now(
|
|
101
|
+
timezone.utc
|
|
102
|
+
) - self.last_failure_time > timedelta(seconds=self.recovery_timeout):
|
|
105
103
|
self.state = CircuitState.HALF_OPEN
|
|
106
104
|
self.logger.info(
|
|
107
105
|
"Circuit breaker transitioning to HALF_OPEN for testing"
|
|
@@ -127,7 +125,7 @@ class CircuitBreaker:
|
|
|
127
125
|
def record_failure(self):
|
|
128
126
|
"""Record failed execution."""
|
|
129
127
|
self.failure_count += 1
|
|
130
|
-
self.last_failure_time = datetime.now()
|
|
128
|
+
self.last_failure_time = datetime.now(timezone.utc)
|
|
131
129
|
|
|
132
130
|
if self.state == CircuitState.HALF_OPEN:
|
|
133
131
|
# Test failed, go back to OPEN
|
|
@@ -184,7 +182,7 @@ class SocketIOConnectionPool:
|
|
|
184
182
|
# Health monitoring
|
|
185
183
|
self.health_thread = None
|
|
186
184
|
self.health_running = False
|
|
187
|
-
self.last_health_check = datetime.now()
|
|
185
|
+
self.last_health_check = datetime.now(timezone.utc)
|
|
188
186
|
|
|
189
187
|
# Server configuration
|
|
190
188
|
self.server_url = None
|
|
@@ -311,7 +309,7 @@ class SocketIOConnectionPool:
|
|
|
311
309
|
self.server_url = f"http://localhost:{port}"
|
|
312
310
|
self.logger.debug(f"Detected Socket.IO server on port {port}")
|
|
313
311
|
return
|
|
314
|
-
except:
|
|
312
|
+
except Exception:
|
|
315
313
|
continue
|
|
316
314
|
|
|
317
315
|
# Fall back to default
|
|
@@ -375,7 +373,7 @@ class SocketIOConnectionPool:
|
|
|
375
373
|
# Check if connection is still valid
|
|
376
374
|
for conn_id, stats in self.connection_stats.items():
|
|
377
375
|
if stats.is_connected:
|
|
378
|
-
stats.last_used = datetime.now()
|
|
376
|
+
stats.last_used = datetime.now(timezone.utc)
|
|
379
377
|
return client
|
|
380
378
|
|
|
381
379
|
# Create new connection if under limit
|
|
@@ -581,7 +579,7 @@ class SocketIOConnectionPool:
|
|
|
581
579
|
loop.stop()
|
|
582
580
|
loop.run_until_complete(loop.shutdown_asyncgens())
|
|
583
581
|
loop.close()
|
|
584
|
-
except:
|
|
582
|
+
except Exception:
|
|
585
583
|
pass
|
|
586
584
|
|
|
587
585
|
async def _connect_client(self, client: socketio.AsyncClient):
|
|
@@ -623,7 +621,7 @@ class SocketIOConnectionPool:
|
|
|
623
621
|
self._check_connections_health()
|
|
624
622
|
|
|
625
623
|
# Update last health check time
|
|
626
|
-
self.last_health_check = datetime.now()
|
|
624
|
+
self.last_health_check = datetime.now(timezone.utc)
|
|
627
625
|
|
|
628
626
|
except Exception as e:
|
|
629
627
|
self.logger.error(f"Health monitor error: {e}")
|
|
@@ -655,7 +653,9 @@ class SocketIOConnectionPool:
|
|
|
655
653
|
continue
|
|
656
654
|
|
|
657
655
|
# 3. Connection idle for too long (>5 minutes)
|
|
658
|
-
idle_time = (
|
|
656
|
+
idle_time = (
|
|
657
|
+
datetime.now(timezone.utc) - stats.last_used
|
|
658
|
+
).total_seconds()
|
|
659
659
|
if idle_time > 300 and conn_id not in [
|
|
660
660
|
id for id, _ in enumerate(self.available_connections)
|
|
661
661
|
]:
|
claude_mpm/core/types.py
CHANGED
|
@@ -16,7 +16,7 @@ Module-specific types should remain in their respective modules.
|
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
from dataclasses import dataclass
|
|
19
|
-
from datetime import datetime
|
|
19
|
+
from datetime import datetime, timezone
|
|
20
20
|
from enum import Enum
|
|
21
21
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
22
22
|
|
|
@@ -206,7 +206,7 @@ class TaskInfo:
|
|
|
206
206
|
if self.metadata is None:
|
|
207
207
|
self.metadata = {}
|
|
208
208
|
if self.created_at is None:
|
|
209
|
-
self.created_at = datetime.now()
|
|
209
|
+
self.created_at = datetime.now(timezone.utc)
|
|
210
210
|
if self.updated_at is None:
|
|
211
211
|
self.updated_at = self.created_at
|
|
212
212
|
|
|
@@ -30,7 +30,7 @@ import json
|
|
|
30
30
|
import logging
|
|
31
31
|
import time
|
|
32
32
|
from dataclasses import asdict, dataclass
|
|
33
|
-
from datetime import datetime
|
|
33
|
+
from datetime import datetime, timezone
|
|
34
34
|
from enum import Enum
|
|
35
35
|
from pathlib import Path
|
|
36
36
|
from typing import Any, Dict, List, Optional, Set, Union
|
|
@@ -718,7 +718,7 @@ class UnifiedAgentRegistry:
|
|
|
718
718
|
|
|
719
719
|
export_data = {
|
|
720
720
|
"metadata": {
|
|
721
|
-
"export_time": datetime.now().isoformat(),
|
|
721
|
+
"export_time": datetime.now(timezone.utc).isoformat(),
|
|
722
722
|
"total_agents": len(self.registry),
|
|
723
723
|
"discovery_paths": [str(p) for p in self.discovery_paths],
|
|
724
724
|
},
|
claude_mpm/core/unified_paths.py
CHANGED
|
@@ -166,7 +166,7 @@ class PathContext:
|
|
|
166
166
|
|
|
167
167
|
@staticmethod
|
|
168
168
|
@lru_cache(maxsize=1)
|
|
169
|
-
def detect_deployment_context() -> DeploymentContext:
|
|
169
|
+
def detect_deployment_context() -> DeploymentContext: # noqa: PLR0911
|
|
170
170
|
"""Detect the current deployment context.
|
|
171
171
|
|
|
172
172
|
Priority order:
|
|
@@ -20,7 +20,7 @@ import subprocess
|
|
|
20
20
|
import sys
|
|
21
21
|
import threading
|
|
22
22
|
from dataclasses import asdict, dataclass
|
|
23
|
-
from datetime import datetime
|
|
23
|
+
from datetime import datetime, timezone
|
|
24
24
|
from pathlib import Path
|
|
25
25
|
from queue import Queue
|
|
26
26
|
from typing import Any, Dict, List, Optional
|
|
@@ -41,7 +41,7 @@ class AnalysisRequest:
|
|
|
41
41
|
|
|
42
42
|
def __post_init__(self):
|
|
43
43
|
if self.timestamp is None:
|
|
44
|
-
self.timestamp = datetime.
|
|
44
|
+
self.timestamp = datetime.now(timezone.utc)
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
class CodeAnalysisRunner:
|
|
@@ -433,7 +433,7 @@ class CodeAnalysisRunner:
|
|
|
433
433
|
if self.server:
|
|
434
434
|
# Add timestamp if not present
|
|
435
435
|
if "timestamp" not in data:
|
|
436
|
-
data["timestamp"] = datetime.
|
|
436
|
+
data["timestamp"] = datetime.now(timezone.utc).isoformat()
|
|
437
437
|
|
|
438
438
|
# Broadcast to all clients
|
|
439
439
|
self.server.broadcast_event(event_type, data)
|
|
@@ -450,6 +450,6 @@ class CodeAnalysisRunner:
|
|
|
450
450
|
{
|
|
451
451
|
"request_id": request_id,
|
|
452
452
|
"message": message,
|
|
453
|
-
"timestamp": datetime.
|
|
453
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
454
454
|
},
|
|
455
455
|
)
|