claude-mpm 3.7.8__py3-none-any.whl → 3.8.1__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.
Files changed (93) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_PM.md +0 -106
  3. claude_mpm/agents/INSTRUCTIONS.md +0 -96
  4. claude_mpm/agents/MEMORY.md +88 -0
  5. claude_mpm/agents/WORKFLOW.md +86 -0
  6. claude_mpm/agents/templates/code_analyzer.json +2 -2
  7. claude_mpm/agents/templates/data_engineer.json +1 -1
  8. claude_mpm/agents/templates/documentation.json +1 -1
  9. claude_mpm/agents/templates/engineer.json +1 -1
  10. claude_mpm/agents/templates/ops.json +1 -1
  11. claude_mpm/agents/templates/qa.json +1 -1
  12. claude_mpm/agents/templates/research.json +1 -1
  13. claude_mpm/agents/templates/security.json +1 -1
  14. claude_mpm/agents/templates/ticketing.json +2 -7
  15. claude_mpm/agents/templates/version_control.json +1 -1
  16. claude_mpm/agents/templates/web_qa.json +2 -2
  17. claude_mpm/agents/templates/web_ui.json +2 -2
  18. claude_mpm/cli/__init__.py +2 -2
  19. claude_mpm/cli/commands/__init__.py +2 -1
  20. claude_mpm/cli/commands/tickets.py +596 -19
  21. claude_mpm/cli/parser.py +217 -5
  22. claude_mpm/config/__init__.py +30 -39
  23. claude_mpm/config/socketio_config.py +8 -5
  24. claude_mpm/constants.py +13 -0
  25. claude_mpm/core/__init__.py +8 -18
  26. claude_mpm/core/cache.py +596 -0
  27. claude_mpm/core/claude_runner.py +166 -622
  28. claude_mpm/core/config.py +5 -1
  29. claude_mpm/core/constants.py +339 -0
  30. claude_mpm/core/container.py +461 -22
  31. claude_mpm/core/exceptions.py +392 -0
  32. claude_mpm/core/framework_loader.py +208 -94
  33. claude_mpm/core/interactive_session.py +432 -0
  34. claude_mpm/core/interfaces.py +424 -0
  35. claude_mpm/core/lazy.py +467 -0
  36. claude_mpm/core/logging_config.py +444 -0
  37. claude_mpm/core/oneshot_session.py +465 -0
  38. claude_mpm/core/optimized_agent_loader.py +485 -0
  39. claude_mpm/core/optimized_startup.py +490 -0
  40. claude_mpm/core/service_registry.py +52 -26
  41. claude_mpm/core/socketio_pool.py +162 -5
  42. claude_mpm/core/types.py +292 -0
  43. claude_mpm/core/typing_utils.py +477 -0
  44. claude_mpm/hooks/claude_hooks/hook_handler.py +213 -99
  45. claude_mpm/init.py +2 -1
  46. claude_mpm/services/__init__.py +78 -14
  47. claude_mpm/services/agent/__init__.py +24 -0
  48. claude_mpm/services/agent/deployment.py +2548 -0
  49. claude_mpm/services/agent/management.py +598 -0
  50. claude_mpm/services/agent/registry.py +813 -0
  51. claude_mpm/services/agents/deployment/agent_deployment.py +587 -268
  52. claude_mpm/services/agents/memory/agent_memory_manager.py +156 -1
  53. claude_mpm/services/async_session_logger.py +8 -3
  54. claude_mpm/services/communication/__init__.py +21 -0
  55. claude_mpm/services/communication/socketio.py +1933 -0
  56. claude_mpm/services/communication/websocket.py +479 -0
  57. claude_mpm/services/core/__init__.py +123 -0
  58. claude_mpm/services/core/base.py +247 -0
  59. claude_mpm/services/core/interfaces.py +951 -0
  60. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +23 -23
  61. claude_mpm/services/framework_claude_md_generator.py +3 -2
  62. claude_mpm/services/health_monitor.py +4 -3
  63. claude_mpm/services/hook_service.py +64 -4
  64. claude_mpm/services/infrastructure/__init__.py +21 -0
  65. claude_mpm/services/infrastructure/logging.py +202 -0
  66. claude_mpm/services/infrastructure/monitoring.py +893 -0
  67. claude_mpm/services/memory/indexed_memory.py +648 -0
  68. claude_mpm/services/project/__init__.py +21 -0
  69. claude_mpm/services/project/analyzer.py +864 -0
  70. claude_mpm/services/project/registry.py +608 -0
  71. claude_mpm/services/project_analyzer.py +95 -2
  72. claude_mpm/services/recovery_manager.py +15 -9
  73. claude_mpm/services/socketio/__init__.py +25 -0
  74. claude_mpm/services/socketio/handlers/__init__.py +25 -0
  75. claude_mpm/services/socketio/handlers/base.py +121 -0
  76. claude_mpm/services/socketio/handlers/connection.py +198 -0
  77. claude_mpm/services/socketio/handlers/file.py +213 -0
  78. claude_mpm/services/socketio/handlers/git.py +723 -0
  79. claude_mpm/services/socketio/handlers/memory.py +27 -0
  80. claude_mpm/services/socketio/handlers/project.py +25 -0
  81. claude_mpm/services/socketio/handlers/registry.py +145 -0
  82. claude_mpm/services/socketio_client_manager.py +12 -7
  83. claude_mpm/services/socketio_server.py +156 -30
  84. claude_mpm/services/ticket_manager.py +170 -7
  85. claude_mpm/utils/error_handler.py +1 -1
  86. claude_mpm/validation/agent_validator.py +27 -14
  87. claude_mpm/validation/frontmatter_validator.py +231 -0
  88. {claude_mpm-3.7.8.dist-info → claude_mpm-3.8.1.dist-info}/METADATA +58 -21
  89. {claude_mpm-3.7.8.dist-info → claude_mpm-3.8.1.dist-info}/RECORD +93 -53
  90. {claude_mpm-3.7.8.dist-info → claude_mpm-3.8.1.dist-info}/WHEEL +0 -0
  91. {claude_mpm-3.7.8.dist-info → claude_mpm-3.8.1.dist-info}/entry_points.txt +0 -0
  92. {claude_mpm-3.7.8.dist-info → claude_mpm-3.8.1.dist-info}/licenses/LICENSE +0 -0
  93. {claude_mpm-3.7.8.dist-info → claude_mpm-3.8.1.dist-info}/top_level.txt +0 -0
@@ -45,31 +45,31 @@ Task(description="[task description]", subagent_type="[agent-type]")
45
45
 
46
46
  **Valid subagent_type values (use lowercase format for Claude Code compatibility):**
47
47
 
48
- **Required lowercase format (Claude Code expects these exact values):**
49
- - `subagent_type="research"` - For investigation and analysis
48
+ **Required format (Claude Code expects these exact values from deployed agent YAML names):**
49
+ - `subagent_type="research-agent"` - For investigation and analysis
50
50
  - `subagent_type="engineer"` - For coding and implementation
51
- - `subagent_type="qa"` - For testing and quality assurance
52
- - `subagent_type="documentation"` - For docs and guides
53
- - `subagent_type="security"` - For security assessments
54
- - `subagent_type="ops"` - For deployment and infrastructure
55
- - `subagent_type="version_control"` - For git and version management (use underscore, not hyphen)
56
- - `subagent_type="data_engineer"` - For data processing and APIs (use underscore, not hyphen)
51
+ - `subagent_type="qa-agent"` - For testing and quality assurance
52
+ - `subagent_type="documentation-agent"` - For docs and guides
53
+ - `subagent_type="security-agent"` - For security assessments
54
+ - `subagent_type="ops-agent"` - For deployment and infrastructure
55
+ - `subagent_type="version-control"` - For git and version management
56
+ - `subagent_type="data-engineer"` - For data processing and APIs
57
57
  - `subagent_type="pm"` - For project management coordination
58
58
  - `subagent_type="test_integration"` - For integration testing
59
59
 
60
- **Note:** Claude Code's Task tool requires exact lowercase agent names. Capitalized formats like "Research" or "Engineer" will be rejected with an error.
60
+ **Note:** Claude Code's Task tool requires exact agent names as defined in the deployed agent YAML frontmatter. The names must match exactly - including hyphens where specified.
61
61
 
62
- **Examples of Proper Task Tool Usage (use lowercase format only):**
63
- - ✅ `Task(description="Update framework documentation", subagent_type="documentation")`
64
- - ✅ `Task(description="Execute test suite validation", subagent_type="qa")`
65
- - ✅ `Task(description="Create feature branch and sync", subagent_type="version_control")` (use underscore)
66
- - ✅ `Task(description="Investigate performance patterns", subagent_type="research")`
62
+ **Examples of Proper Task Tool Usage (must match deployed agent YAML names):**
63
+ - ✅ `Task(description="Update framework documentation", subagent_type="documentation-agent")`
64
+ - ✅ `Task(description="Execute test suite validation", subagent_type="qa-agent")`
65
+ - ✅ `Task(description="Create feature branch and sync", subagent_type="version-control")`
66
+ - ✅ `Task(description="Investigate performance patterns", subagent_type="research-agent")`
67
67
  - ✅ `Task(description="Implement authentication system", subagent_type="engineer")`
68
- - ✅ `Task(description="Configure database and optimize queries", subagent_type="data_engineer")` (use underscore)
68
+ - ✅ `Task(description="Configure database and optimize queries", subagent_type="data-engineer")`
69
69
  - ✅ `Task(description="Coordinate project tasks", subagent_type="pm")`
70
- - ❌ `Task(description="Analyze code patterns", subagent_type="Research")` (WRONG - will be rejected)
71
- - ❌ `Task(description="Update API docs", subagent_type="Documentation")` (WRONG - will be rejected)
72
- - ❌ `Task(description="Create release tags", subagent_type="Version Control")` (WRONG - will be rejected)
70
+ - ❌ `Task(description="Analyze code patterns", subagent_type="research")` (WRONG - missing '-agent' suffix)
71
+ - ❌ `Task(description="Update API docs", subagent_type="documentation")` (WRONG - missing '-agent' suffix)
72
+ - ❌ `Task(description="Create release tags", subagent_type="version_control")` (WRONG - should be 'version-control')
73
73
 
74
74
  ### 🚨 MANDATORY: THREE SHORTCUT COMMANDS
75
75
 
@@ -98,11 +98,11 @@ Task(description="[task description]", subagent_type="[agent-type]")
98
98
  - ☐ [Data Engineer] Validate data integrity and verify API connectivity
99
99
  - ☐ [Version Control] Apply semantic version bump and create release tags
100
100
 
101
- # Corresponding Task Tool delegations (use lowercase with underscores):
102
- Task(description="Generate changelog and analyze version impact", subagent_type="documentation")
103
- Task(description="Execute full test suite and quality validation", subagent_type="qa")
104
- Task(description="Validate data integrity and verify API connectivity", subagent_type="data_engineer")
105
- Task(description="Apply semantic version bump and create release tags", subagent_type="version_control")
101
+ # Corresponding Task Tool delegations (must match deployed agent names):
102
+ Task(description="Generate changelog and analyze version impact", subagent_type="documentation-agent")
103
+ Task(description="Execute full test suite and quality validation", subagent_type="qa-agent")
104
+ Task(description="Validate data integrity and verify API connectivity", subagent_type="data-engineer")
105
+ Task(description="Apply semantic version bump and create release tags", subagent_type="version-control")
106
106
 
107
107
  # Update TodoWrite status based on agent completions
108
108
  ```
@@ -19,6 +19,7 @@ from typing import Dict, Optional, Any, List, Tuple, Callable
19
19
  from dataclasses import dataclass, field
20
20
 
21
21
  from claude_mpm.core.config_paths import ConfigPaths
22
+ from claude_mpm.core.constants import SystemLimits
22
23
 
23
24
  logger = logging.getLogger(__name__)
24
25
 
@@ -450,8 +451,8 @@ class FrameworkClaudeMdGenerator:
450
451
  suggestions = []
451
452
 
452
453
  # Check minimum length
453
- if len(content) < 1000:
454
- errors.append("Content seems too short")
454
+ if len(content) < SystemLimits.MIN_CONTENT_LENGTH:
455
+ errors.append(f"Content seems too short (minimum {SystemLimits.MIN_CONTENT_LENGTH} characters)")
455
456
 
456
457
  # Check for required sections
457
458
  required_sections = [
@@ -27,6 +27,7 @@ from pathlib import Path
27
27
  from typing import Any, Dict, List, Optional, Callable, Union
28
28
  import json
29
29
  import socket
30
+ from claude_mpm.core.constants import ResourceLimits, TimeoutConfig
30
31
 
31
32
  try:
32
33
  import psutil
@@ -196,7 +197,7 @@ class ProcessResourceChecker(HealthChecker):
196
197
 
197
198
  # CPU usage
198
199
  try:
199
- cpu_percent = self.process.cpu_percent(interval=0.1)
200
+ cpu_percent = self.process.cpu_percent(interval=TimeoutConfig.CPU_SAMPLE_INTERVAL)
200
201
  cpu_status = HealthStatus.HEALTHY
201
202
  if cpu_percent > self.cpu_threshold:
202
203
  cpu_status = HealthStatus.WARNING if cpu_percent < self.cpu_threshold * 1.2 else HealthStatus.CRITICAL
@@ -219,7 +220,7 @@ class ProcessResourceChecker(HealthChecker):
219
220
  # Memory usage
220
221
  try:
221
222
  memory_info = self.process.memory_info()
222
- memory_mb = memory_info.rss / (1024 * 1024)
223
+ memory_mb = memory_info.rss / ResourceLimits.BYTES_TO_MB
223
224
  memory_status = HealthStatus.HEALTHY
224
225
  if memory_mb > self.memory_threshold_mb:
225
226
  memory_status = HealthStatus.WARNING if memory_mb < self.memory_threshold_mb * 1.2 else HealthStatus.CRITICAL
@@ -234,7 +235,7 @@ class ProcessResourceChecker(HealthChecker):
234
235
 
235
236
  metrics.append(HealthMetric(
236
237
  name="memory_vms_mb",
237
- value=round(memory_info.vms / (1024 * 1024), 2),
238
+ value=round(memory_info.vms / ResourceLimits.BYTES_TO_MB, 2),
238
239
  status=HealthStatus.HEALTHY,
239
240
  unit="MB"
240
241
  ))
@@ -16,7 +16,7 @@ import time
16
16
  from datetime import datetime
17
17
 
18
18
  from claude_mpm.core.config import Config
19
- from claude_mpm.core.logger import get_logger
19
+ from claude_mpm.core.logging_config import get_logger, log_operation
20
20
  from claude_mpm.hooks.base_hook import (
21
21
  BaseHook,
22
22
  PreDelegationHook,
@@ -25,9 +25,10 @@ from claude_mpm.hooks.base_hook import (
25
25
  HookResult,
26
26
  HookType
27
27
  )
28
+ from claude_mpm.core.interfaces import HookServiceInterface
28
29
 
29
30
 
30
- class HookService:
31
+ class HookService(HookServiceInterface):
31
32
  """Service for managing and executing hooks in the delegation workflow.
32
33
 
33
34
  WHY: Provides a centralized place to register and execute hooks, ensuring
@@ -47,7 +48,7 @@ class HookService:
47
48
  config: Optional configuration object for controlling hook behavior
48
49
  """
49
50
  self.config = config or Config()
50
- self.logger = get_logger("hook_service")
51
+ self.logger = get_logger(__name__)
51
52
 
52
53
  # Separate lists for different hook types for performance
53
54
  self.pre_delegation_hooks: List[PreDelegationHook] = []
@@ -359,4 +360,63 @@ class HookService:
359
360
  return True
360
361
 
361
362
  self.logger.warning(f"Hook not found: {hook_name}")
362
- return False
363
+ return False
364
+
365
+ # ================================================================================
366
+ # Interface Adapter Methods
367
+ # ================================================================================
368
+ # These methods are added to comply with HookServiceInterface
369
+
370
+ def get_registered_hooks(self) -> Dict[str, List[Any]]:
371
+ """Get all registered hooks by type.
372
+
373
+ WHY: This method provides interface compliance by exposing the
374
+ registered hooks for inspection and debugging purposes.
375
+
376
+ Returns:
377
+ Dictionary mapping hook types to lists of hooks
378
+ """
379
+ return {
380
+ "pre_delegation": [
381
+ {
382
+ "name": hook.name,
383
+ "priority": hook.priority,
384
+ "enabled": hook.enabled,
385
+ "type": "pre_delegation"
386
+ }
387
+ for hook in self.pre_delegation_hooks
388
+ ],
389
+ "post_delegation": [
390
+ {
391
+ "name": hook.name,
392
+ "priority": hook.priority,
393
+ "enabled": hook.enabled,
394
+ "type": "post_delegation"
395
+ }
396
+ for hook in self.post_delegation_hooks
397
+ ]
398
+ }
399
+
400
+ def clear_hooks(self, hook_type: Optional[str] = None) -> None:
401
+ """Clear registered hooks.
402
+
403
+ WHY: This method provides interface compliance by allowing bulk
404
+ removal of hooks, useful for testing and cleanup scenarios.
405
+
406
+ Args:
407
+ hook_type: Optional specific hook type to clear, or None for all
408
+ """
409
+ if hook_type is None or hook_type == "pre_delegation":
410
+ count = len(self.pre_delegation_hooks)
411
+ self.pre_delegation_hooks.clear()
412
+ if count > 0:
413
+ self.logger.info(f"Cleared {count} pre-delegation hooks")
414
+
415
+ if hook_type is None or hook_type == "post_delegation":
416
+ count = len(self.post_delegation_hooks)
417
+ self.post_delegation_hooks.clear()
418
+ if count > 0:
419
+ self.logger.info(f"Cleared {count} post-delegation hooks")
420
+
421
+ if hook_type and hook_type not in ["pre_delegation", "post_delegation"]:
422
+ self.logger.warning(f"Unknown hook type: {hook_type}")
@@ -0,0 +1,21 @@
1
+ """
2
+ Infrastructure Services Module
3
+ =============================
4
+
5
+ This module contains infrastructure-related services including
6
+ logging, monitoring, and system health management.
7
+
8
+ Part of TSK-0046: Service Layer Architecture Reorganization
9
+
10
+ Services:
11
+ - LoggingService: Centralized logging with structured output
12
+ - HealthMonitor: System health monitoring and alerting
13
+ """
14
+
15
+ from .logging import LoggingService
16
+ from .monitoring import HealthMonitor
17
+
18
+ __all__ = [
19
+ 'LoggingService',
20
+ 'HealthMonitor',
21
+ ]
@@ -0,0 +1,202 @@
1
+ """
2
+ Logging Service for Claude MPM Framework
3
+ ========================================
4
+
5
+ This module provides centralized logging services and utilities.
6
+
7
+ Part of TSK-0046: Service Layer Architecture Reorganization
8
+ """
9
+
10
+ from typing import Any, Dict, Optional, List
11
+ from pathlib import Path
12
+ import logging
13
+ import json
14
+ from datetime import datetime
15
+
16
+ from claude_mpm.services.core import SyncBaseService, ILoggingService
17
+ from claude_mpm.utils.logger import get_logger
18
+
19
+
20
+ class LoggingService(SyncBaseService, ILoggingService):
21
+ """
22
+ Centralized logging service for the Claude MPM framework.
23
+
24
+ This service provides:
25
+ - Structured logging with JSON output
26
+ - Log rotation and archival
27
+ - Performance metrics logging
28
+ - Audit trail capabilities
29
+ """
30
+
31
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
32
+ """
33
+ Initialize logging service.
34
+
35
+ Args:
36
+ config: Logging configuration
37
+ """
38
+ super().__init__("LoggingService", config)
39
+ self.log_dir = Path(self.get_config('log_dir', '.claude-mpm/logs'))
40
+ self.log_level = self.get_config('log_level', 'INFO')
41
+ self.structured_logging = self.get_config('structured_logging', True)
42
+ self._log_handlers = []
43
+
44
+ def initialize(self) -> bool:
45
+ """Initialize the logging service."""
46
+ try:
47
+ # Create log directory if it doesn't exist
48
+ self.log_dir.mkdir(parents=True, exist_ok=True)
49
+
50
+ # Set up log rotation if configured
51
+ if self.get_config('enable_rotation', True):
52
+ self._setup_rotation()
53
+
54
+ # Set up structured logging if enabled
55
+ if self.structured_logging:
56
+ self._setup_structured_logging()
57
+
58
+ self._initialized = True
59
+ self.log_info("Logging service initialized successfully")
60
+ return True
61
+
62
+ except Exception as e:
63
+ self.log_error(f"Failed to initialize logging service: {e}")
64
+ return False
65
+
66
+ def shutdown(self) -> None:
67
+ """Shutdown the logging service."""
68
+ try:
69
+ # Flush all handlers
70
+ for handler in self._log_handlers:
71
+ handler.flush()
72
+ handler.close()
73
+
74
+ self._log_handlers.clear()
75
+ self._shutdown = True
76
+ self.log_info("Logging service shutdown successfully")
77
+
78
+ except Exception as e:
79
+ self.log_error(f"Error during logging service shutdown: {e}")
80
+
81
+ def log(self, level: str, message: str, **context) -> None:
82
+ """
83
+ Log a message with context.
84
+
85
+ Args:
86
+ level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
87
+ message: Log message
88
+ **context: Additional context to include
89
+ """
90
+ logger = get_logger(context.get('component', 'default'))
91
+
92
+ if self.structured_logging:
93
+ log_entry = {
94
+ 'timestamp': datetime.utcnow().isoformat(),
95
+ 'level': level,
96
+ 'message': message,
97
+ 'context': context
98
+ }
99
+ message = json.dumps(log_entry)
100
+
101
+ log_method = getattr(logger, level.lower(), logger.info)
102
+ log_method(message)
103
+
104
+ def log_performance(self, operation: str, duration: float, **metrics) -> None:
105
+ """
106
+ Log performance metrics.
107
+
108
+ Args:
109
+ operation: Name of the operation
110
+ duration: Duration in seconds
111
+ **metrics: Additional performance metrics
112
+ """
113
+ self.log('INFO', f"Performance: {operation}",
114
+ operation=operation,
115
+ duration_ms=duration * 1000,
116
+ **metrics)
117
+
118
+ def log_audit(self, action: str, user: str, **details) -> None:
119
+ """
120
+ Log an audit event.
121
+
122
+ Args:
123
+ action: Action performed
124
+ user: User who performed the action
125
+ **details: Additional audit details
126
+ """
127
+ self.log('INFO', f"Audit: {action} by {user}",
128
+ audit=True,
129
+ action=action,
130
+ user=user,
131
+ **details)
132
+
133
+ def get_logs(self, level: Optional[str] = None,
134
+ component: Optional[str] = None,
135
+ limit: int = 100) -> List[Dict[str, Any]]:
136
+ """
137
+ Retrieve recent logs.
138
+
139
+ Args:
140
+ level: Filter by log level
141
+ component: Filter by component
142
+ limit: Maximum number of logs to return
143
+
144
+ Returns:
145
+ List of log entries
146
+ """
147
+ # This would typically read from log files or a log aggregation service
148
+ # For now, return empty list as placeholder
149
+ return []
150
+
151
+ def _setup_rotation(self) -> None:
152
+ """Set up log rotation."""
153
+ from logging.handlers import RotatingFileHandler
154
+
155
+ max_bytes = self.get_config('max_bytes', 10 * 1024 * 1024) # 10MB
156
+ backup_count = self.get_config('backup_count', 5)
157
+
158
+ handler = RotatingFileHandler(
159
+ self.log_dir / 'claude_mpm.log',
160
+ maxBytes=max_bytes,
161
+ backupCount=backup_count
162
+ )
163
+
164
+ formatter = logging.Formatter(
165
+ '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
166
+ )
167
+ handler.setFormatter(formatter)
168
+
169
+ self._log_handlers.append(handler)
170
+ logging.getLogger().addHandler(handler)
171
+
172
+ def _setup_structured_logging(self) -> None:
173
+ """Set up structured JSON logging."""
174
+ import logging.config
175
+
176
+ config = {
177
+ 'version': 1,
178
+ 'disable_existing_loggers': False,
179
+ 'formatters': {
180
+ 'json': {
181
+ 'class': 'pythonjsonlogger.jsonlogger.JsonFormatter',
182
+ 'format': '%(asctime)s %(name)s %(levelname)s %(message)s'
183
+ }
184
+ },
185
+ 'handlers': {
186
+ 'json_file': {
187
+ 'class': 'logging.FileHandler',
188
+ 'filename': str(self.log_dir / 'structured.json'),
189
+ 'formatter': 'json'
190
+ }
191
+ },
192
+ 'root': {
193
+ 'handlers': ['json_file'],
194
+ 'level': self.log_level
195
+ }
196
+ }
197
+
198
+ try:
199
+ logging.config.dictConfig(config)
200
+ except ImportError:
201
+ # If pythonjsonlogger is not available, fall back to regular logging
202
+ self.log_warning("pythonjsonlogger not available, using standard logging")