claude-mpm 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (159) hide show
  1. claude_mpm/__init__.py +17 -0
  2. claude_mpm/__main__.py +14 -0
  3. claude_mpm/_version.py +32 -0
  4. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +88 -0
  5. claude_mpm/agents/INSTRUCTIONS.md +375 -0
  6. claude_mpm/agents/__init__.py +118 -0
  7. claude_mpm/agents/agent_loader.py +621 -0
  8. claude_mpm/agents/agent_loader_integration.py +229 -0
  9. claude_mpm/agents/agents_metadata.py +204 -0
  10. claude_mpm/agents/base_agent.json +27 -0
  11. claude_mpm/agents/base_agent_loader.py +519 -0
  12. claude_mpm/agents/schema/agent_schema.json +160 -0
  13. claude_mpm/agents/system_agent_config.py +587 -0
  14. claude_mpm/agents/templates/__init__.py +101 -0
  15. claude_mpm/agents/templates/data_engineer_agent.json +46 -0
  16. claude_mpm/agents/templates/documentation_agent.json +45 -0
  17. claude_mpm/agents/templates/engineer_agent.json +49 -0
  18. claude_mpm/agents/templates/ops_agent.json +46 -0
  19. claude_mpm/agents/templates/qa_agent.json +45 -0
  20. claude_mpm/agents/templates/research_agent.json +49 -0
  21. claude_mpm/agents/templates/security_agent.json +46 -0
  22. claude_mpm/agents/templates/update-optimized-specialized-agents.json +374 -0
  23. claude_mpm/agents/templates/version_control_agent.json +46 -0
  24. claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +6 -0
  25. claude_mpm/cli.py +655 -0
  26. claude_mpm/cli_main.py +13 -0
  27. claude_mpm/cli_module/__init__.py +15 -0
  28. claude_mpm/cli_module/args.py +222 -0
  29. claude_mpm/cli_module/commands.py +203 -0
  30. claude_mpm/cli_module/migration_example.py +183 -0
  31. claude_mpm/cli_module/refactoring_guide.md +253 -0
  32. claude_mpm/cli_old/__init__.py +1 -0
  33. claude_mpm/cli_old/ticket_cli.py +102 -0
  34. claude_mpm/config/__init__.py +5 -0
  35. claude_mpm/config/hook_config.py +42 -0
  36. claude_mpm/constants.py +150 -0
  37. claude_mpm/core/__init__.py +45 -0
  38. claude_mpm/core/agent_name_normalizer.py +248 -0
  39. claude_mpm/core/agent_registry.py +627 -0
  40. claude_mpm/core/agent_registry.py.bak +312 -0
  41. claude_mpm/core/agent_session_manager.py +273 -0
  42. claude_mpm/core/base_service.py +747 -0
  43. claude_mpm/core/base_service.py.bak +406 -0
  44. claude_mpm/core/config.py +334 -0
  45. claude_mpm/core/config_aliases.py +292 -0
  46. claude_mpm/core/container.py +347 -0
  47. claude_mpm/core/factories.py +281 -0
  48. claude_mpm/core/framework_loader.py +472 -0
  49. claude_mpm/core/injectable_service.py +206 -0
  50. claude_mpm/core/interfaces.py +539 -0
  51. claude_mpm/core/logger.py +468 -0
  52. claude_mpm/core/minimal_framework_loader.py +107 -0
  53. claude_mpm/core/mixins.py +150 -0
  54. claude_mpm/core/service_registry.py +299 -0
  55. claude_mpm/core/session_manager.py +190 -0
  56. claude_mpm/core/simple_runner.py +511 -0
  57. claude_mpm/core/tool_access_control.py +173 -0
  58. claude_mpm/hooks/README.md +243 -0
  59. claude_mpm/hooks/__init__.py +5 -0
  60. claude_mpm/hooks/base_hook.py +154 -0
  61. claude_mpm/hooks/builtin/__init__.py +1 -0
  62. claude_mpm/hooks/builtin/logging_hook_example.py +165 -0
  63. claude_mpm/hooks/builtin/post_delegation_hook_example.py +124 -0
  64. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +125 -0
  65. claude_mpm/hooks/builtin/submit_hook_example.py +100 -0
  66. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +237 -0
  67. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +239 -0
  68. claude_mpm/hooks/builtin/workflow_start_hook.py +181 -0
  69. claude_mpm/hooks/hook_client.py +264 -0
  70. claude_mpm/hooks/hook_runner.py +370 -0
  71. claude_mpm/hooks/json_rpc_executor.py +259 -0
  72. claude_mpm/hooks/json_rpc_hook_client.py +319 -0
  73. claude_mpm/hooks/tool_call_interceptor.py +204 -0
  74. claude_mpm/init.py +246 -0
  75. claude_mpm/orchestration/SUBPROCESS_DESIGN.md +66 -0
  76. claude_mpm/orchestration/__init__.py +6 -0
  77. claude_mpm/orchestration/archive/direct_orchestrator.py +195 -0
  78. claude_mpm/orchestration/archive/factory.py +215 -0
  79. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +188 -0
  80. claude_mpm/orchestration/archive/hook_integration_example.py +178 -0
  81. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +826 -0
  82. claude_mpm/orchestration/archive/orchestrator.py +501 -0
  83. claude_mpm/orchestration/archive/pexpect_orchestrator.py +252 -0
  84. claude_mpm/orchestration/archive/pty_orchestrator.py +270 -0
  85. claude_mpm/orchestration/archive/simple_orchestrator.py +82 -0
  86. claude_mpm/orchestration/archive/subprocess_orchestrator.py +801 -0
  87. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +278 -0
  88. claude_mpm/orchestration/archive/wrapper_orchestrator.py +187 -0
  89. claude_mpm/scripts/__init__.py +1 -0
  90. claude_mpm/scripts/ticket.py +269 -0
  91. claude_mpm/services/__init__.py +10 -0
  92. claude_mpm/services/agent_deployment.py +955 -0
  93. claude_mpm/services/agent_lifecycle_manager.py +948 -0
  94. claude_mpm/services/agent_management_service.py +596 -0
  95. claude_mpm/services/agent_modification_tracker.py +841 -0
  96. claude_mpm/services/agent_profile_loader.py +606 -0
  97. claude_mpm/services/agent_registry.py +677 -0
  98. claude_mpm/services/base_agent_manager.py +380 -0
  99. claude_mpm/services/framework_agent_loader.py +337 -0
  100. claude_mpm/services/framework_claude_md_generator/README.md +92 -0
  101. claude_mpm/services/framework_claude_md_generator/__init__.py +206 -0
  102. claude_mpm/services/framework_claude_md_generator/content_assembler.py +151 -0
  103. claude_mpm/services/framework_claude_md_generator/content_validator.py +126 -0
  104. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +137 -0
  105. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +106 -0
  106. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +582 -0
  107. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +97 -0
  108. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +27 -0
  109. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +23 -0
  110. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +23 -0
  111. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +20 -0
  112. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +26 -0
  113. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +30 -0
  114. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +37 -0
  115. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +111 -0
  116. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +89 -0
  117. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +39 -0
  118. claude_mpm/services/framework_claude_md_generator/section_manager.py +106 -0
  119. claude_mpm/services/framework_claude_md_generator/version_manager.py +121 -0
  120. claude_mpm/services/framework_claude_md_generator.py +621 -0
  121. claude_mpm/services/hook_service.py +388 -0
  122. claude_mpm/services/hook_service_manager.py +223 -0
  123. claude_mpm/services/json_rpc_hook_manager.py +92 -0
  124. claude_mpm/services/parent_directory_manager/README.md +83 -0
  125. claude_mpm/services/parent_directory_manager/__init__.py +577 -0
  126. claude_mpm/services/parent_directory_manager/backup_manager.py +258 -0
  127. claude_mpm/services/parent_directory_manager/config_manager.py +210 -0
  128. claude_mpm/services/parent_directory_manager/deduplication_manager.py +279 -0
  129. claude_mpm/services/parent_directory_manager/framework_protector.py +143 -0
  130. claude_mpm/services/parent_directory_manager/operations.py +186 -0
  131. claude_mpm/services/parent_directory_manager/state_manager.py +624 -0
  132. claude_mpm/services/parent_directory_manager/template_deployer.py +579 -0
  133. claude_mpm/services/parent_directory_manager/validation_manager.py +378 -0
  134. claude_mpm/services/parent_directory_manager/version_control_helper.py +339 -0
  135. claude_mpm/services/parent_directory_manager/version_manager.py +222 -0
  136. claude_mpm/services/shared_prompt_cache.py +819 -0
  137. claude_mpm/services/ticket_manager.py +213 -0
  138. claude_mpm/services/ticket_manager_di.py +318 -0
  139. claude_mpm/services/ticketing_service_original.py +508 -0
  140. claude_mpm/services/version_control/VERSION +1 -0
  141. claude_mpm/services/version_control/__init__.py +70 -0
  142. claude_mpm/services/version_control/branch_strategy.py +670 -0
  143. claude_mpm/services/version_control/conflict_resolution.py +744 -0
  144. claude_mpm/services/version_control/git_operations.py +784 -0
  145. claude_mpm/services/version_control/semantic_versioning.py +703 -0
  146. claude_mpm/ui/__init__.py +1 -0
  147. claude_mpm/ui/rich_terminal_ui.py +295 -0
  148. claude_mpm/ui/terminal_ui.py +328 -0
  149. claude_mpm/utils/__init__.py +16 -0
  150. claude_mpm/utils/config_manager.py +468 -0
  151. claude_mpm/utils/import_migration_example.py +80 -0
  152. claude_mpm/utils/imports.py +182 -0
  153. claude_mpm/utils/path_operations.py +357 -0
  154. claude_mpm/utils/paths.py +289 -0
  155. claude_mpm-0.3.0.dist-info/METADATA +290 -0
  156. claude_mpm-0.3.0.dist-info/RECORD +159 -0
  157. claude_mpm-0.3.0.dist-info/WHEEL +5 -0
  158. claude_mpm-0.3.0.dist-info/entry_points.txt +4 -0
  159. claude_mpm-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,468 @@
1
+ """Unified logging system for Claude MPM.
2
+
3
+ This module consolidates features from:
4
+ - utils/logger.py (simple console/file logging)
5
+ - core/logging_config.py (Rich formatting, JSON, streaming)
6
+ - core/project_logger.py (project-local logging, statistics)
7
+ """
8
+
9
+ import json
10
+ import logging
11
+ import logging.handlers
12
+ import sys
13
+ import time
14
+ from pathlib import Path
15
+ from datetime import datetime
16
+ from typing import Optional, Dict, Any, List
17
+ from enum import Enum
18
+ from collections import defaultdict
19
+ import functools
20
+
21
+ # Optional Rich support
22
+ try:
23
+ from rich.logging import RichHandler
24
+ from rich.console import Console
25
+ HAS_RICH = True
26
+ except ImportError:
27
+ HAS_RICH = False
28
+
29
+
30
+ class LogLevel(Enum):
31
+ """Log levels for different verbosity."""
32
+ OFF = "off"
33
+ DEBUG = "debug"
34
+ INFO = "info"
35
+ WARNING = "warning"
36
+ ERROR = "error"
37
+ CRITICAL = "critical"
38
+
39
+
40
+ class StreamingHandler(logging.StreamHandler):
41
+ """
42
+ Custom handler for single-line streaming INFO messages.
43
+
44
+ Shows progress indicators that update in place using carriage returns
45
+ while keeping ERROR and WARNING messages on separate lines.
46
+ """
47
+
48
+ def __init__(self, stream=None):
49
+ super().__init__(stream)
50
+ self._last_info_message = False
51
+ self._info_line_active = False
52
+
53
+ def emit(self, record):
54
+ """Emit a log record with streaming support for INFO messages."""
55
+ try:
56
+ msg = self.format(record)
57
+ stream = self.stream
58
+
59
+ # Handle different log levels
60
+ if record.levelno == logging.INFO:
61
+ # For INFO messages, use carriage return for streaming
62
+ if self._info_line_active:
63
+ # Clear the previous line by overwriting with spaces
64
+ stream.write('\r' + ' ' * 100 + '\r')
65
+
66
+ # Write INFO message with carriage return (no newline)
67
+ stream.write(f'\r{msg}')
68
+ stream.flush()
69
+ self._info_line_active = True
70
+ self._last_info_message = True
71
+
72
+ else:
73
+ # For WARNING, ERROR, CRITICAL - always on new lines
74
+ if self._info_line_active:
75
+ # Finish the INFO line first
76
+ stream.write('\n')
77
+ self._info_line_active = False
78
+
79
+ stream.write(f'{msg}\n')
80
+ stream.flush()
81
+ self._last_info_message = False
82
+
83
+ except (KeyboardInterrupt, SystemExit):
84
+ raise
85
+ except:
86
+ self.handleError(record)
87
+
88
+ def finalize_info_line(self):
89
+ """
90
+ Finalize any active INFO line by adding a newline.
91
+ Call this when you want to ensure the final INFO message remains visible.
92
+ """
93
+ if self._info_line_active:
94
+ self.stream.write('\n')
95
+ self.stream.flush()
96
+ self._info_line_active = False
97
+
98
+
99
+ class JsonFormatter(logging.Formatter):
100
+ """JSON formatter for structured logging."""
101
+
102
+ def format(self, record: logging.LogRecord) -> str:
103
+ """Format log record as JSON."""
104
+ log_entry = {
105
+ "timestamp": self.formatTime(record, self.datefmt),
106
+ "level": record.levelname,
107
+ "logger": record.name,
108
+ "message": record.getMessage(),
109
+ "module": record.module,
110
+ "function": record.funcName,
111
+ "line": record.lineno,
112
+ }
113
+
114
+ # Add exception info if present
115
+ if record.exc_info:
116
+ log_entry["exception"] = self.formatException(record.exc_info)
117
+
118
+ # Add extra fields
119
+ for key, value in record.__dict__.items():
120
+ if key not in {
121
+ "name", "msg", "args", "levelname", "levelno", "pathname",
122
+ "filename", "module", "lineno", "funcName", "created",
123
+ "msecs", "relativeCreated", "thread", "threadName",
124
+ "processName", "process", "message", "exc_info",
125
+ "exc_text", "stack_info"
126
+ }:
127
+ log_entry[key] = value
128
+
129
+ return json.dumps(log_entry, default=str)
130
+
131
+
132
+ def setup_logging(
133
+ name: str = "claude_mpm",
134
+ level: str = "INFO",
135
+ log_dir: Optional[Path] = None,
136
+ log_file: Optional[Path] = None,
137
+ console_output: bool = True,
138
+ file_output: bool = True,
139
+ use_rich: bool = False,
140
+ json_format: bool = False,
141
+ use_streaming: bool = False,
142
+ ) -> logging.Logger:
143
+ """
144
+ Set up logging with both console and file handlers.
145
+
146
+ Args:
147
+ name: Logger name
148
+ level: Logging level (OFF, DEBUG, INFO, WARNING, ERROR, CRITICAL)
149
+ log_dir: Directory for log files (defaults to ~/.claude-mpm/logs)
150
+ log_file: Specific log file path (overrides log_dir)
151
+ console_output: Enable console output
152
+ file_output: Enable file output
153
+ use_rich: Use Rich handler for colored console output
154
+ json_format: Use JSON format for structured logging
155
+ use_streaming: Use streaming handler for single-line INFO messages
156
+
157
+ Returns:
158
+ Configured logger
159
+ """
160
+ logger = logging.getLogger(name)
161
+
162
+ # Handle OFF level
163
+ if level.upper() == "OFF":
164
+ logger.setLevel(logging.CRITICAL + 1) # Higher than CRITICAL
165
+ logger.handlers.clear()
166
+ return logger
167
+
168
+ # Set log level
169
+ log_level = getattr(logging, level.upper(), logging.INFO)
170
+ logger.setLevel(log_level)
171
+
172
+ # Prevent duplicate handlers
173
+ logger.handlers.clear()
174
+
175
+ # Create formatters
176
+ if json_format:
177
+ formatter = JsonFormatter()
178
+ else:
179
+ detailed_formatter = logging.Formatter(
180
+ '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
181
+ )
182
+ simple_formatter = logging.Formatter('%(levelname)s: %(message)s')
183
+
184
+ # Console handler
185
+ if console_output:
186
+ if use_streaming:
187
+ # Use streaming handler for single-line INFO messages
188
+ console_handler = StreamingHandler(sys.stdout)
189
+ console_handler.setFormatter(simple_formatter)
190
+ elif use_rich and HAS_RICH and not json_format:
191
+ console = Console(stderr=True)
192
+ console_handler = RichHandler(console=console, show_time=True, show_path=True, markup=True)
193
+ console_handler.setFormatter(logging.Formatter(fmt="%(message)s", datefmt="[%X]"))
194
+ else:
195
+ console_handler = logging.StreamHandler(sys.stdout)
196
+ console_handler.setFormatter(formatter if json_format else simple_formatter)
197
+
198
+ console_handler.setLevel(logging.INFO)
199
+ logger.addHandler(console_handler)
200
+
201
+ # File handler
202
+ if file_output and level.upper() in ["INFO", "DEBUG"]:
203
+ if log_file:
204
+ # Use specific log file
205
+ log_file = Path(log_file)
206
+ log_file.parent.mkdir(parents=True, exist_ok=True)
207
+
208
+ # Use rotating file handler
209
+ file_handler = logging.handlers.RotatingFileHandler(
210
+ log_file, maxBytes=10 * 1024 * 1024, backupCount=5 # 10 MB
211
+ )
212
+ else:
213
+ # Use default log directory
214
+ if log_dir is None:
215
+ log_dir = Path.home() / ".claude-mpm" / "logs"
216
+
217
+ log_dir.mkdir(parents=True, exist_ok=True)
218
+
219
+ # Create timestamped log file
220
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
221
+ log_file = log_dir / f"mpm_{timestamp}.log"
222
+
223
+ file_handler = logging.FileHandler(log_file)
224
+
225
+ # Also create a symlink to latest log
226
+ latest_link = log_dir / "latest.log"
227
+ if latest_link.exists() and latest_link.is_symlink():
228
+ latest_link.unlink()
229
+ if not latest_link.exists():
230
+ latest_link.symlink_to(log_file.name)
231
+
232
+ file_handler.setLevel(logging.DEBUG)
233
+ file_handler.setFormatter(formatter if json_format else detailed_formatter)
234
+ logger.addHandler(file_handler)
235
+
236
+ # Prevent propagation to root logger
237
+ logger.propagate = False
238
+
239
+ return logger
240
+
241
+
242
+ def get_logger(name: str) -> logging.Logger:
243
+ """Get a logger instance."""
244
+ return logging.getLogger(f"claude_mpm.{name}")
245
+
246
+
247
+ def setup_streaming_logger(name: str, level: str = "INFO") -> logging.Logger:
248
+ """
249
+ Convenience function to setup a logger with streaming INFO support.
250
+
251
+ Args:
252
+ name: Logger name
253
+ level: Log level (default: INFO)
254
+
255
+ Returns:
256
+ Logger configured with streaming handler
257
+ """
258
+ return setup_logging(
259
+ name=name,
260
+ level=level,
261
+ use_rich=False,
262
+ use_streaming=True
263
+ )
264
+
265
+
266
+ def finalize_streaming_logs(logger: logging.Logger):
267
+ """
268
+ Finalize any active streaming INFO lines for a logger.
269
+
270
+ This ensures the final INFO message remains visible by adding
271
+ a newline to complete any streaming output.
272
+ """
273
+ for handler in logger.handlers:
274
+ if isinstance(handler, StreamingHandler):
275
+ handler.finalize_info_line()
276
+
277
+
278
+ def log_performance(func):
279
+ """Decorator to log function execution time."""
280
+ @functools.wraps(func)
281
+ def wrapper(*args, **kwargs):
282
+ logger = get_logger(func.__module__)
283
+ start_time = time.time()
284
+
285
+ try:
286
+ result = func(*args, **kwargs)
287
+ execution_time = time.time() - start_time
288
+ logger.debug(f"{func.__name__} executed in {execution_time:.3f}s")
289
+ return result
290
+ except Exception as e:
291
+ execution_time = time.time() - start_time
292
+ logger.error(f"{func.__name__} failed after {execution_time:.3f}s: {e}")
293
+ raise
294
+
295
+ return wrapper
296
+
297
+
298
+ async def log_async_performance(func):
299
+ """Decorator to log async function execution time."""
300
+ @functools.wraps(func)
301
+ async def wrapper(*args, **kwargs):
302
+ logger = get_logger(func.__module__)
303
+ start_time = time.time()
304
+
305
+ try:
306
+ result = await func(*args, **kwargs)
307
+ execution_time = time.time() - start_time
308
+ logger.debug(f"{func.__name__} executed in {execution_time:.3f}s")
309
+ return result
310
+ except Exception as e:
311
+ execution_time = time.time() - start_time
312
+ logger.error(f"{func.__name__} failed after {execution_time:.3f}s: {e}")
313
+ raise
314
+
315
+ return wrapper
316
+
317
+
318
+ class ProjectLogger:
319
+ """
320
+ Manages project-local logging in ./claude-mpm directory.
321
+
322
+ This is a simplified version of the original ProjectLogger,
323
+ focused on essential features for backwards compatibility.
324
+ """
325
+
326
+ def __init__(self,
327
+ project_dir: Optional[Path] = None,
328
+ log_level: str = "INFO",
329
+ create_structure: bool = True):
330
+ """Initialize project logger.
331
+
332
+ Args:
333
+ project_dir: Project directory (defaults to cwd)
334
+ log_level: Logging level (INFO, DEBUG, OFF)
335
+ create_structure: Whether to create directory structure
336
+ """
337
+ self.project_dir = project_dir or Path.cwd()
338
+ self.claude_mpm_dir = self.project_dir / ".claude-mpm"
339
+ self.log_level = LogLevel(log_level.lower())
340
+
341
+ # Basic directory structure
342
+ self.dirs = {
343
+ "base": self.claude_mpm_dir,
344
+ "logs": self.claude_mpm_dir / "logs",
345
+ "logs_system": self.claude_mpm_dir / "logs" / "system",
346
+ "logs_agents": self.claude_mpm_dir / "logs" / "agents",
347
+ "logs_sessions": self.claude_mpm_dir / "logs" / "sessions",
348
+ "stats": self.claude_mpm_dir / "stats",
349
+ }
350
+
351
+ if create_structure:
352
+ for path in self.dirs.values():
353
+ path.mkdir(parents=True, exist_ok=True)
354
+
355
+ # Create session directory
356
+ self.session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
357
+ self.session_start_time = datetime.now()
358
+ self.session_dir = self.dirs["logs_sessions"] / self.session_id
359
+ self.session_dir.mkdir(parents=True, exist_ok=True)
360
+
361
+ # Statistics tracking
362
+ self.stats = defaultdict(lambda: {
363
+ "total_calls": 0,
364
+ "total_tokens": 0,
365
+ "total_time_seconds": 0,
366
+ "by_agent": defaultdict(lambda: {
367
+ "calls": 0,
368
+ "tokens": 0,
369
+ "time_seconds": 0,
370
+ "success_rate": 0.0,
371
+ "tasks": []
372
+ })
373
+ })
374
+
375
+ def log_system(self, message: str, level: str = "INFO", component: str = "general"):
376
+ """Log system-level message."""
377
+ if self.log_level == LogLevel.OFF:
378
+ return
379
+
380
+ timestamp = datetime.now().isoformat()
381
+ log_entry = {
382
+ "timestamp": timestamp,
383
+ "level": level,
384
+ "component": component,
385
+ "message": message
386
+ }
387
+
388
+ # Write to daily log file
389
+ log_file = self.dirs["logs_system"] / f"{datetime.now().strftime('%Y%m%d')}.jsonl"
390
+ with open(log_file, 'a') as f:
391
+ f.write(json.dumps(log_entry) + '\n')
392
+
393
+ def log_agent_invocation(self,
394
+ agent: str,
395
+ task: str,
396
+ prompt: str,
397
+ response: str,
398
+ execution_time: float,
399
+ tokens: int,
400
+ success: bool = True,
401
+ metadata: Optional[Dict[str, Any]] = None):
402
+ """Log agent invocation with configurable detail level."""
403
+ if self.log_level == LogLevel.OFF:
404
+ return
405
+
406
+ timestamp = datetime.now().isoformat()
407
+
408
+ # Update statistics
409
+ today = datetime.now().strftime("%Y-%m-%d")
410
+ self.stats[today]["total_calls"] += 1
411
+ self.stats[today]["total_tokens"] += tokens
412
+ self.stats[today]["total_time_seconds"] += execution_time
413
+
414
+ agent_stats = self.stats[today]["by_agent"][agent.lower()]
415
+ agent_stats["calls"] += 1
416
+ agent_stats["tokens"] += tokens
417
+ agent_stats["time_seconds"] += execution_time
418
+
419
+ # Prepare log entry
420
+ log_entry = {
421
+ "timestamp": timestamp,
422
+ "agent": agent,
423
+ "task": task[:200],
424
+ "execution_time": execution_time,
425
+ "tokens": tokens,
426
+ "success": success,
427
+ "metadata": metadata or {}
428
+ }
429
+
430
+ # Add full details in DEBUG mode
431
+ if self.log_level == LogLevel.DEBUG:
432
+ log_entry["prompt"] = prompt
433
+ log_entry["response"] = response
434
+
435
+ # Write to agent-specific log
436
+ agent_log_dir = self.dirs["logs_agents"] / agent.lower()
437
+ agent_log_dir.mkdir(exist_ok=True)
438
+
439
+ daily_log = agent_log_dir / f"{datetime.now().strftime('%Y%m%d')}.jsonl"
440
+ with open(daily_log, 'a') as f:
441
+ f.write(json.dumps(log_entry) + '\n')
442
+
443
+ def get_session_summary(self) -> Dict[str, Any]:
444
+ """Get summary of current session."""
445
+ return {
446
+ "session_id": self.session_id,
447
+ "session_dir": str(self.session_dir),
448
+ "start_time": self.session_id,
449
+ "stats": self.stats.get(datetime.now().strftime("%Y-%m-%d"), {})
450
+ }
451
+
452
+
453
+ # Singleton instance for project logger
454
+ _project_logger = None
455
+
456
+ def get_project_logger(log_level: str = "INFO") -> ProjectLogger:
457
+ """Get or create the project logger singleton.
458
+
459
+ Args:
460
+ log_level: Log level (INFO, DEBUG, OFF)
461
+
462
+ Returns:
463
+ ProjectLogger instance
464
+ """
465
+ global _project_logger
466
+ if _project_logger is None:
467
+ _project_logger = ProjectLogger(log_level=log_level)
468
+ return _project_logger
@@ -0,0 +1,107 @@
1
+ """Minimal framework loader for better performance."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional, Dict, Any
5
+ import logging
6
+
7
+ try:
8
+ from ..core.logger import get_logger
9
+ except ImportError:
10
+ from core.logger import get_logger
11
+
12
+
13
+ class MinimalFrameworkLoader:
14
+ """Load a minimal framework for non-interactive mode."""
15
+
16
+ def __init__(
17
+ self,
18
+ framework_path: Optional[Path] = None,
19
+ agents_dir: Optional[Path] = None,
20
+ ):
21
+ """Initialize the minimal loader."""
22
+ self.logger = get_logger("minimal_framework_loader")
23
+ self.framework_path = framework_path or self._detect_framework_path()
24
+ self.agents_dir = agents_dir
25
+
26
+ def _detect_framework_path(self) -> Optional[Path]:
27
+ """Detect the claude-mpm framework path."""
28
+ # Same detection logic as main loader
29
+ candidates = [
30
+ Path.cwd() / "claude-mpm",
31
+ Path.cwd().parent / "claude-mpm",
32
+ Path.home() / "Projects" / "claude-mpm",
33
+ ]
34
+
35
+ for candidate in candidates:
36
+ if candidate.exists() and (candidate / "src").exists():
37
+ return candidate
38
+
39
+ return None
40
+
41
+ def get_framework_instructions(self) -> str:
42
+ """Get minimal framework instructions."""
43
+ # Load working directory INSTRUCTIONS.md (or legacy CLAUDE.md) if exists
44
+ working_claude = ""
45
+ working_instructions_path = Path.cwd() / "INSTRUCTIONS.md"
46
+ working_claude_path = Path.cwd() / "CLAUDE.md" # Legacy support
47
+
48
+ if working_instructions_path.exists():
49
+ try:
50
+ working_claude = working_instructions_path.read_text()
51
+ except:
52
+ pass
53
+ elif working_claude_path.exists():
54
+ try:
55
+ working_claude = working_claude_path.read_text()
56
+ except:
57
+ pass
58
+
59
+ # Build minimal framework
60
+ framework = f"""# Claude MPM Framework
61
+
62
+ You are a multi-agent orchestrator in the Claude MPM framework.
63
+
64
+ ## Core Responsibilities
65
+ 1. **Orchestrate** - Delegate ALL implementation work to specialized agents via Task Tool
66
+ 2. **Coordinate** - Manage multi-agent workflows and cross-agent collaboration
67
+ 3. **Track** - Extract TODO/BUG/FEATURE items for ticket creation
68
+ 4. **Oversee** - Maintain project visibility and strategic oversight
69
+ 5. **Never Implement** - You NEVER write code or perform direct implementation
70
+
71
+ ## Available Agents
72
+ - **Engineer Agent**: Code implementation and development
73
+ - **QA Agent**: Testing and quality assurance
74
+ - **Documentation Agent**: Documentation and changelogs
75
+ - **Research Agent**: Investigation and analysis
76
+ - **Security Agent**: Security analysis and protection
77
+ - **Version Control Agent**: Git operations and version management
78
+ - **Ops Agent**: Deployment and operations
79
+ - **Data Engineer Agent**: Data management and AI API integration
80
+
81
+ ## Task Tool Format
82
+ When delegating, use this format:
83
+ ```
84
+ **[Agent Name]**: [Clear task description]
85
+
86
+ **Task**: [Detailed breakdown]
87
+ **Context**: [Relevant context]
88
+ **Expected Results**: [Specific deliverables]
89
+ ```
90
+
91
+ ## Ticket Patterns
92
+ Extract from: TODO:, BUG:, FEATURE:, ISSUE:
93
+
94
+ """
95
+
96
+ # Add working directory instructions if present
97
+ if working_claude:
98
+ framework += f"\n## Working Directory Instructions\n{working_claude}\n"
99
+
100
+ return framework
101
+
102
+ def get_agent_list(self) -> list:
103
+ """Get list of available agents."""
104
+ return [
105
+ "engineer", "qa", "documentation", "research",
106
+ "security", "version_control", "ops", "data_engineer"
107
+ ]