claude-mpm 4.13.2__py3-none-any.whl → 4.18.2__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 (250) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_ENGINEER.md +286 -0
  3. claude_mpm/agents/BASE_PM.md +48 -17
  4. claude_mpm/agents/OUTPUT_STYLE.md +329 -11
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +227 -8
  6. claude_mpm/agents/agent_loader.py +17 -5
  7. claude_mpm/agents/frontmatter_validator.py +284 -253
  8. claude_mpm/agents/templates/agentic-coder-optimizer.json +9 -2
  9. claude_mpm/agents/templates/api_qa.json +7 -1
  10. claude_mpm/agents/templates/clerk-ops.json +8 -1
  11. claude_mpm/agents/templates/code_analyzer.json +4 -1
  12. claude_mpm/agents/templates/dart_engineer.json +11 -1
  13. claude_mpm/agents/templates/data_engineer.json +11 -1
  14. claude_mpm/agents/templates/documentation.json +6 -1
  15. claude_mpm/agents/templates/engineer.json +18 -1
  16. claude_mpm/agents/templates/gcp_ops_agent.json +8 -1
  17. claude_mpm/agents/templates/golang_engineer.json +11 -1
  18. claude_mpm/agents/templates/java_engineer.json +12 -2
  19. claude_mpm/agents/templates/local_ops_agent.json +1217 -6
  20. claude_mpm/agents/templates/nextjs_engineer.json +11 -1
  21. claude_mpm/agents/templates/ops.json +8 -1
  22. claude_mpm/agents/templates/php-engineer.json +11 -1
  23. claude_mpm/agents/templates/project_organizer.json +10 -3
  24. claude_mpm/agents/templates/prompt-engineer.json +5 -1
  25. claude_mpm/agents/templates/python_engineer.json +11 -1
  26. claude_mpm/agents/templates/qa.json +7 -1
  27. claude_mpm/agents/templates/react_engineer.json +11 -1
  28. claude_mpm/agents/templates/refactoring_engineer.json +8 -1
  29. claude_mpm/agents/templates/research.json +4 -1
  30. claude_mpm/agents/templates/ruby-engineer.json +11 -1
  31. claude_mpm/agents/templates/rust_engineer.json +11 -1
  32. claude_mpm/agents/templates/security.json +6 -1
  33. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  34. claude_mpm/agents/templates/ticketing.json +6 -1
  35. claude_mpm/agents/templates/typescript_engineer.json +11 -1
  36. claude_mpm/agents/templates/vercel_ops_agent.json +8 -1
  37. claude_mpm/agents/templates/version_control.json +8 -1
  38. claude_mpm/agents/templates/web_qa.json +7 -1
  39. claude_mpm/agents/templates/web_ui.json +11 -1
  40. claude_mpm/cli/__init__.py +34 -706
  41. claude_mpm/cli/commands/agent_manager.py +25 -12
  42. claude_mpm/cli/commands/agent_state_manager.py +186 -0
  43. claude_mpm/cli/commands/agents.py +204 -148
  44. claude_mpm/cli/commands/aggregate.py +7 -3
  45. claude_mpm/cli/commands/analyze.py +9 -4
  46. claude_mpm/cli/commands/analyze_code.py +7 -2
  47. claude_mpm/cli/commands/auto_configure.py +7 -9
  48. claude_mpm/cli/commands/config.py +47 -13
  49. claude_mpm/cli/commands/configure.py +294 -1788
  50. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  51. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  52. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  53. claude_mpm/cli/commands/configure_models.py +18 -0
  54. claude_mpm/cli/commands/configure_navigation.py +167 -0
  55. claude_mpm/cli/commands/configure_paths.py +104 -0
  56. claude_mpm/cli/commands/configure_persistence.py +254 -0
  57. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  58. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  59. claude_mpm/cli/commands/configure_validators.py +73 -0
  60. claude_mpm/cli/commands/local_deploy.py +537 -0
  61. claude_mpm/cli/commands/memory.py +54 -20
  62. claude_mpm/cli/commands/mpm_init.py +39 -25
  63. claude_mpm/cli/commands/mpm_init_handler.py +8 -3
  64. claude_mpm/cli/executor.py +202 -0
  65. claude_mpm/cli/helpers.py +105 -0
  66. claude_mpm/cli/interactive/__init__.py +3 -0
  67. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  68. claude_mpm/cli/parsers/__init__.py +7 -1
  69. claude_mpm/cli/parsers/base_parser.py +98 -3
  70. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  71. claude_mpm/cli/shared/output_formatters.py +28 -19
  72. claude_mpm/cli/startup.py +481 -0
  73. claude_mpm/cli/utils.py +52 -1
  74. claude_mpm/commands/mpm-help.md +3 -0
  75. claude_mpm/commands/mpm-version.md +113 -0
  76. claude_mpm/commands/mpm.md +1 -0
  77. claude_mpm/config/agent_config.py +2 -2
  78. claude_mpm/config/model_config.py +428 -0
  79. claude_mpm/core/base_service.py +13 -12
  80. claude_mpm/core/enums.py +452 -0
  81. claude_mpm/core/factories.py +1 -1
  82. claude_mpm/core/instruction_reinforcement_hook.py +2 -1
  83. claude_mpm/core/interactive_session.py +9 -3
  84. claude_mpm/core/logging_config.py +6 -2
  85. claude_mpm/core/oneshot_session.py +8 -4
  86. claude_mpm/core/optimized_agent_loader.py +3 -3
  87. claude_mpm/core/output_style_manager.py +12 -192
  88. claude_mpm/core/service_registry.py +5 -1
  89. claude_mpm/core/types.py +2 -9
  90. claude_mpm/core/typing_utils.py +7 -6
  91. claude_mpm/dashboard/static/js/dashboard.js +0 -14
  92. claude_mpm/dashboard/templates/index.html +3 -41
  93. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  94. claude_mpm/hooks/instruction_reinforcement.py +7 -2
  95. claude_mpm/models/resume_log.py +340 -0
  96. claude_mpm/services/agents/auto_config_manager.py +10 -11
  97. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  98. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  99. claude_mpm/services/agents/deployment/agent_validator.py +17 -1
  100. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  101. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  102. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  103. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
  104. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  105. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  106. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
  107. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  108. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
  109. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  110. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  111. claude_mpm/services/agents/local_template_manager.py +1 -1
  112. claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
  113. claude_mpm/services/agents/registry/modification_tracker.py +5 -2
  114. claude_mpm/services/command_handler_service.py +11 -5
  115. claude_mpm/services/core/interfaces/__init__.py +74 -2
  116. claude_mpm/services/core/interfaces/health.py +172 -0
  117. claude_mpm/services/core/interfaces/model.py +281 -0
  118. claude_mpm/services/core/interfaces/process.py +372 -0
  119. claude_mpm/services/core/interfaces/restart.py +307 -0
  120. claude_mpm/services/core/interfaces/stability.py +260 -0
  121. claude_mpm/services/core/models/__init__.py +33 -0
  122. claude_mpm/services/core/models/agent_config.py +12 -28
  123. claude_mpm/services/core/models/health.py +162 -0
  124. claude_mpm/services/core/models/process.py +235 -0
  125. claude_mpm/services/core/models/restart.py +302 -0
  126. claude_mpm/services/core/models/stability.py +264 -0
  127. claude_mpm/services/core/path_resolver.py +23 -7
  128. claude_mpm/services/diagnostics/__init__.py +2 -2
  129. claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
  130. claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
  131. claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
  132. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
  133. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  134. claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
  135. claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
  136. claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
  137. claude_mpm/services/diagnostics/checks/mcp_services_check.py +36 -31
  138. claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
  139. claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
  140. claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
  141. claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
  142. claude_mpm/services/diagnostics/models.py +19 -24
  143. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
  144. claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
  145. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  146. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  147. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  148. claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
  149. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  150. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  151. claude_mpm/services/local_ops/__init__.py +163 -0
  152. claude_mpm/services/local_ops/crash_detector.py +257 -0
  153. claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
  154. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  155. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  156. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  157. claude_mpm/services/local_ops/health_manager.py +430 -0
  158. claude_mpm/services/local_ops/log_monitor.py +396 -0
  159. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  160. claude_mpm/services/local_ops/process_manager.py +595 -0
  161. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  162. claude_mpm/services/local_ops/restart_manager.py +401 -0
  163. claude_mpm/services/local_ops/restart_policy.py +387 -0
  164. claude_mpm/services/local_ops/state_manager.py +372 -0
  165. claude_mpm/services/local_ops/unified_manager.py +600 -0
  166. claude_mpm/services/mcp_config_manager.py +9 -4
  167. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  168. claude_mpm/services/mcp_gateway/core/base.py +18 -31
  169. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
  170. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
  171. claude_mpm/services/memory_hook_service.py +4 -1
  172. claude_mpm/services/model/__init__.py +147 -0
  173. claude_mpm/services/model/base_provider.py +365 -0
  174. claude_mpm/services/model/claude_provider.py +412 -0
  175. claude_mpm/services/model/model_router.py +453 -0
  176. claude_mpm/services/model/ollama_provider.py +415 -0
  177. claude_mpm/services/monitor/daemon_manager.py +3 -2
  178. claude_mpm/services/monitor/handlers/dashboard.py +2 -1
  179. claude_mpm/services/monitor/handlers/hooks.py +2 -1
  180. claude_mpm/services/monitor/management/lifecycle.py +3 -2
  181. claude_mpm/services/monitor/server.py +2 -1
  182. claude_mpm/services/session_management_service.py +3 -2
  183. claude_mpm/services/session_manager.py +205 -1
  184. claude_mpm/services/shared/async_service_base.py +16 -27
  185. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  186. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  187. claude_mpm/services/socketio/handlers/hook.py +13 -2
  188. claude_mpm/services/socketio/handlers/registry.py +4 -2
  189. claude_mpm/services/socketio/server/main.py +10 -8
  190. claude_mpm/services/subprocess_launcher_service.py +14 -5
  191. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
  192. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
  193. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
  194. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
  195. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
  196. claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
  197. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
  198. claude_mpm/services/unified/deployment_strategies/local.py +6 -5
  199. claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
  200. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
  201. claude_mpm/services/unified/interfaces.py +3 -1
  202. claude_mpm/services/unified/unified_analyzer.py +14 -10
  203. claude_mpm/services/unified/unified_config.py +2 -1
  204. claude_mpm/services/unified/unified_deployment.py +9 -4
  205. claude_mpm/services/version_service.py +104 -1
  206. claude_mpm/skills/__init__.py +21 -0
  207. claude_mpm/skills/bundled/__init__.py +6 -0
  208. claude_mpm/skills/bundled/api-documentation.md +393 -0
  209. claude_mpm/skills/bundled/async-testing.md +571 -0
  210. claude_mpm/skills/bundled/code-review.md +143 -0
  211. claude_mpm/skills/bundled/database-migration.md +199 -0
  212. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  213. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  214. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  215. claude_mpm/skills/bundled/git-workflow.md +414 -0
  216. claude_mpm/skills/bundled/imagemagick.md +204 -0
  217. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  218. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  219. claude_mpm/skills/bundled/pdf.md +141 -0
  220. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  221. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  222. claude_mpm/skills/bundled/security-scanning.md +327 -0
  223. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  224. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  225. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  226. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  227. claude_mpm/skills/bundled/xlsx.md +157 -0
  228. claude_mpm/skills/registry.py +286 -0
  229. claude_mpm/skills/skill_manager.py +310 -0
  230. claude_mpm/tools/code_tree_analyzer.py +177 -141
  231. claude_mpm/tools/code_tree_events.py +4 -2
  232. claude_mpm/utils/agent_dependency_loader.py +2 -2
  233. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +117 -8
  234. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +238 -174
  235. claude_mpm/dashboard/static/css/code-tree.css +0 -1639
  236. claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
  237. claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
  238. claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
  239. claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
  240. claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
  241. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
  242. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  243. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
  244. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  245. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  246. claude_mpm/services/project/analyzer_refactored.py +0 -450
  247. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
  248. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
  249. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
  250. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
@@ -6,12 +6,19 @@ Ensures a single session ID is generated and used across all components.
6
6
 
7
7
  This service addresses race conditions and duplicate session ID generation
8
8
  by providing a single source of truth for session identifiers.
9
+
10
+ Extended with:
11
+ - Token usage tracking and monitoring
12
+ - Resume log generation on session end
13
+ - Context metrics persistence
14
+ - Automatic resume log injection on session startup
9
15
  """
10
16
 
11
17
  import os
12
18
  from datetime import datetime, timezone
19
+ from pathlib import Path
13
20
  from threading import Lock
14
- from typing import Optional
21
+ from typing import Any, Dict, Optional
15
22
 
16
23
  from claude_mpm.core.logging_utils import get_logger
17
24
 
@@ -64,6 +71,24 @@ class SessionManager:
64
71
  self._session_id = self._generate_session_id()
65
72
  self._session_start_time = datetime.now(timezone.utc)
66
73
 
74
+ # Token usage tracking
75
+ self._cumulative_tokens = 0
76
+ self._total_budget = 200000 # Default Claude Code budget
77
+ self._last_stop_reason: Optional[str] = None
78
+
79
+ # Context metrics storage
80
+ self._context_metrics: Dict[str, Any] = {
81
+ "total_budget": self._total_budget,
82
+ "used_tokens": 0,
83
+ "remaining_tokens": self._total_budget,
84
+ "percentage_used": 0.0,
85
+ "stop_reason": None,
86
+ "model": "claude-sonnet-4.5",
87
+ }
88
+
89
+ # Resume log reference (loaded on startup if exists)
90
+ self._resume_log_content: Optional[str] = None
91
+
67
92
  # Mark as initialized
68
93
  self.__class__._initialized = True
69
94
 
@@ -71,6 +96,9 @@ class SessionManager:
71
96
  f"SessionManager initialized with session ID: {self._session_id}"
72
97
  )
73
98
 
99
+ # Check for resume log from previous session
100
+ self._load_resume_log()
101
+
74
102
  def _generate_session_id(self) -> str:
75
103
  """
76
104
  Generate or retrieve a session ID.
@@ -134,6 +162,182 @@ class SessionManager:
134
162
  f"Session ID already set to {session_id}, no change needed"
135
163
  )
136
164
 
165
+ def update_token_usage(
166
+ self,
167
+ input_tokens: int = 0,
168
+ output_tokens: int = 0,
169
+ stop_reason: Optional[str] = None,
170
+ ) -> Dict[str, Any]:
171
+ """
172
+ Update cumulative token usage for the session.
173
+
174
+ Args:
175
+ input_tokens: Input tokens from latest API call
176
+ output_tokens: Output tokens from latest API call
177
+ stop_reason: Stop reason from Claude API
178
+
179
+ Returns:
180
+ Updated context metrics
181
+ """
182
+ with self.__class__._lock:
183
+ # Update cumulative usage
184
+ tokens_used = input_tokens + output_tokens
185
+ self._cumulative_tokens += tokens_used
186
+
187
+ # Update stop reason if provided
188
+ if stop_reason:
189
+ self._last_stop_reason = stop_reason
190
+
191
+ # Calculate metrics
192
+ remaining = max(0, self._total_budget - self._cumulative_tokens)
193
+ percentage = (self._cumulative_tokens / self._total_budget) * 100
194
+
195
+ # Update context metrics
196
+ self._context_metrics = {
197
+ "total_budget": self._total_budget,
198
+ "used_tokens": self._cumulative_tokens,
199
+ "remaining_tokens": remaining,
200
+ "percentage_used": percentage,
201
+ "stop_reason": self._last_stop_reason,
202
+ "model": "claude-sonnet-4.5",
203
+ }
204
+
205
+ logger.debug(
206
+ f"Token usage updated: {self._cumulative_tokens}/{self._total_budget} "
207
+ f"({percentage:.1f}%) - Stop reason: {stop_reason}"
208
+ )
209
+
210
+ return self._context_metrics.copy()
211
+
212
+ def get_context_metrics(self) -> Dict[str, Any]:
213
+ """
214
+ Get current context metrics.
215
+
216
+ Returns:
217
+ Dictionary containing token usage and context metrics
218
+ """
219
+ with self.__class__._lock:
220
+ return self._context_metrics.copy()
221
+
222
+ def get_token_usage_percentage(self) -> float:
223
+ """
224
+ Get current token usage as a percentage (0.0 to 1.0).
225
+
226
+ Returns:
227
+ Token usage percentage
228
+ """
229
+ with self.__class__._lock:
230
+ return self._context_metrics["percentage_used"] / 100.0
231
+
232
+ def should_warn_context_limit(self, threshold: float = 0.70) -> bool:
233
+ """
234
+ Check if context usage has reached warning threshold.
235
+
236
+ Args:
237
+ threshold: Warning threshold (0.0 to 1.0)
238
+
239
+ Returns:
240
+ True if threshold reached
241
+ """
242
+ return self.get_token_usage_percentage() >= threshold
243
+
244
+ def _load_resume_log(self) -> None:
245
+ """
246
+ Load resume log from previous session if it exists.
247
+
248
+ This is called during initialization to check for session continuity.
249
+ """
250
+ try:
251
+ # Lazy import to avoid circular dependencies
252
+ from claude_mpm.services.infrastructure.resume_log_generator import (
253
+ ResumeLogGenerator,
254
+ )
255
+
256
+ generator = ResumeLogGenerator()
257
+
258
+ # Check if there's a resume log for this session
259
+ # (Could be from a previous interrupted session with same ID)
260
+ resume_content = generator.load_resume_log(self._session_id)
261
+
262
+ if resume_content:
263
+ self._resume_log_content = resume_content
264
+ logger.info(f"Loaded resume log for session {self._session_id}")
265
+ else:
266
+ logger.debug("No resume log found for current session")
267
+
268
+ except Exception as e:
269
+ logger.warning(f"Failed to load resume log: {e}")
270
+ # Non-critical error, continue without resume log
271
+
272
+ def get_resume_log_content(self) -> Optional[str]:
273
+ """
274
+ Get resume log content if loaded.
275
+
276
+ Returns:
277
+ Resume log markdown content or None
278
+ """
279
+ with self.__class__._lock:
280
+ return self._resume_log_content
281
+
282
+ def generate_resume_log(
283
+ self,
284
+ session_state: Optional[Dict[str, Any]] = None,
285
+ ) -> Optional[Path]:
286
+ """
287
+ Generate and save resume log for current session.
288
+
289
+ Args:
290
+ session_state: Optional session state data to include
291
+
292
+ Returns:
293
+ Path to saved resume log or None if generation failed
294
+ """
295
+ try:
296
+ # Lazy import to avoid circular dependencies
297
+ from claude_mpm.models.resume_log import ContextMetrics, ResumeLog
298
+ from claude_mpm.services.infrastructure.resume_log_generator import (
299
+ ResumeLogGenerator,
300
+ )
301
+
302
+ generator = ResumeLogGenerator()
303
+
304
+ # Create context metrics from current state
305
+ context_metrics = ContextMetrics(
306
+ total_budget=self._total_budget,
307
+ used_tokens=self._cumulative_tokens,
308
+ remaining_tokens=self._context_metrics["remaining_tokens"],
309
+ percentage_used=self._context_metrics["percentage_used"],
310
+ stop_reason=self._last_stop_reason,
311
+ model=self._context_metrics["model"],
312
+ session_id=self._session_id,
313
+ )
314
+
315
+ if session_state:
316
+ # Generate from provided session state
317
+ resume_log = generator.generate_from_session_state(
318
+ session_id=self._session_id,
319
+ session_state=session_state,
320
+ stop_reason=self._last_stop_reason,
321
+ )
322
+ else:
323
+ # Create minimal resume log
324
+ resume_log = ResumeLog(
325
+ session_id=self._session_id,
326
+ context_metrics=context_metrics,
327
+ mission_summary="Session ended - resume log auto-generated.",
328
+ )
329
+
330
+ if resume_log:
331
+ file_path = generator.save_resume_log(resume_log)
332
+ logger.info(f"Resume log generated and saved: {file_path}")
333
+ return file_path
334
+ logger.warning("Resume log generation returned None")
335
+ return None
336
+
337
+ except Exception as e:
338
+ logger.error(f"Failed to generate resume log: {e}", exc_info=True)
339
+ return None
340
+
137
341
  @classmethod
138
342
  def reset(cls) -> None:
139
343
  """
@@ -4,23 +4,12 @@ Base class for asynchronous services to reduce duplication.
4
4
 
5
5
  import asyncio
6
6
  from abc import ABC, abstractmethod
7
- from enum import Enum
8
7
  from typing import Any, Dict, Optional
9
8
 
9
+ from ...core.enums import ServiceState
10
10
  from ...core.mixins import LoggerMixin
11
11
 
12
12
 
13
- class AsyncServiceState(Enum):
14
- """Standard states for async services."""
15
-
16
- UNINITIALIZED = "uninitialized"
17
- INITIALIZING = "initializing"
18
- RUNNING = "running"
19
- STOPPING = "stopping"
20
- STOPPED = "stopped"
21
- ERROR = "error"
22
-
23
-
24
13
  class AsyncServiceBase(LoggerMixin, ABC):
25
14
  """
26
15
  Base class for asynchronous services.
@@ -45,7 +34,7 @@ class AsyncServiceBase(LoggerMixin, ABC):
45
34
  self.config = config or {}
46
35
 
47
36
  # State management
48
- self._state = AsyncServiceState.UNINITIALIZED
37
+ self._state = ServiceState.UNINITIALIZED
49
38
  self._state_lock = asyncio.Lock()
50
39
 
51
40
  # Background tasks
@@ -57,19 +46,19 @@ class AsyncServiceBase(LoggerMixin, ABC):
57
46
  self._error_count = 0
58
47
 
59
48
  @property
60
- def state(self) -> AsyncServiceState:
49
+ def state(self) -> ServiceState:
61
50
  """Get current service state."""
62
51
  return self._state
63
52
 
64
53
  @property
65
54
  def is_running(self) -> bool:
66
55
  """Check if service is running."""
67
- return self._state == AsyncServiceState.RUNNING
56
+ return self._state == ServiceState.RUNNING
68
57
 
69
58
  @property
70
59
  def is_healthy(self) -> bool:
71
60
  """Check if service is healthy."""
72
- return self._state == AsyncServiceState.RUNNING and self._last_error is None
61
+ return self._state == ServiceState.RUNNING and self._last_error is None
73
62
 
74
63
  async def initialize(self) -> bool:
75
64
  """
@@ -79,22 +68,22 @@ class AsyncServiceBase(LoggerMixin, ABC):
79
68
  True if initialization successful
80
69
  """
81
70
  async with self._state_lock:
82
- if self._state != AsyncServiceState.UNINITIALIZED:
71
+ if self._state != ServiceState.UNINITIALIZED:
83
72
  self.logger.warning(f"Service {self.service_name} already initialized")
84
- return self._state == AsyncServiceState.RUNNING
73
+ return self._state == ServiceState.RUNNING
85
74
 
86
- self._state = AsyncServiceState.INITIALIZING
75
+ self._state = ServiceState.INITIALIZING
87
76
  self.logger.info(f"Initializing service: {self.service_name}")
88
77
 
89
78
  try:
90
79
  success = await self._do_initialize()
91
80
  if success:
92
- self._state = AsyncServiceState.RUNNING
81
+ self._state = ServiceState.RUNNING
93
82
  self.logger.info(
94
83
  f"Service {self.service_name} initialized successfully"
95
84
  )
96
85
  else:
97
- self._state = AsyncServiceState.ERROR
86
+ self._state = ServiceState.ERROR
98
87
  self.logger.error(
99
88
  f"Service {self.service_name} initialization failed"
100
89
  )
@@ -102,7 +91,7 @@ class AsyncServiceBase(LoggerMixin, ABC):
102
91
  return success
103
92
 
104
93
  except Exception as e:
105
- self._state = AsyncServiceState.ERROR
94
+ self._state = ServiceState.ERROR
106
95
  self._last_error = e
107
96
  self._error_count += 1
108
97
  self.logger.error(
@@ -114,10 +103,10 @@ class AsyncServiceBase(LoggerMixin, ABC):
114
103
  async def shutdown(self) -> None:
115
104
  """Shutdown the service gracefully."""
116
105
  async with self._state_lock:
117
- if self._state in (AsyncServiceState.STOPPED, AsyncServiceState.STOPPING):
106
+ if self._state in (ServiceState.STOPPED, ServiceState.STOPPING):
118
107
  return
119
108
 
120
- self._state = AsyncServiceState.STOPPING
109
+ self._state = ServiceState.STOPPING
121
110
  self.logger.info(f"Shutting down service: {self.service_name}")
122
111
 
123
112
  try:
@@ -130,11 +119,11 @@ class AsyncServiceBase(LoggerMixin, ABC):
130
119
  # Service-specific shutdown
131
120
  await self._do_shutdown()
132
121
 
133
- self._state = AsyncServiceState.STOPPED
122
+ self._state = ServiceState.STOPPED
134
123
  self.logger.info(f"Service {self.service_name} shut down successfully")
135
124
 
136
125
  except Exception as e:
137
- self._state = AsyncServiceState.ERROR
126
+ self._state = ServiceState.ERROR
138
127
  self._last_error = e
139
128
  self.logger.error(
140
129
  f"Service {self.service_name} shutdown error: {e}", exc_info=True
@@ -146,7 +135,7 @@ class AsyncServiceBase(LoggerMixin, ABC):
146
135
  await self.shutdown()
147
136
 
148
137
  # Reset state for restart
149
- self._state = AsyncServiceState.UNINITIALIZED
138
+ self._state = ServiceState.UNINITIALIZED
150
139
  self._shutdown_event.clear()
151
140
  self._last_error = None
152
141
 
@@ -4,25 +4,12 @@ Base class for services with complex lifecycle management.
4
4
 
5
5
  import time
6
6
  from abc import ABC, abstractmethod
7
- from enum import Enum
8
7
  from typing import Any, Dict, List, Optional
9
8
 
9
+ from ...core.enums import ServiceState
10
10
  from ...core.mixins import LoggerMixin
11
11
 
12
12
 
13
- class ServiceState(Enum):
14
- """Standard service states."""
15
-
16
- UNINITIALIZED = "uninitialized"
17
- INITIALIZING = "initializing"
18
- INITIALIZED = "initialized"
19
- STARTING = "starting"
20
- RUNNING = "running"
21
- STOPPING = "stopping"
22
- STOPPED = "stopped"
23
- ERROR = "error"
24
-
25
-
26
13
  class LifecycleServiceBase(LoggerMixin, ABC):
27
14
  """
28
15
  Base class for services with complex lifecycle management.
@@ -7,7 +7,9 @@ and maintainability.
7
7
  """
8
8
 
9
9
  from .base import BaseEventHandler
10
- from .code_analysis import CodeAnalysisEventHandler
10
+
11
+ # DISABLED: File Tree interface removed from dashboard
12
+ # from .code_analysis import CodeAnalysisEventHandler
11
13
  from .connection import ConnectionEventHandler
12
14
  from .file import FileEventHandler
13
15
  from .git import GitEventHandler
@@ -17,7 +19,8 @@ from .registry import EventHandlerRegistry
17
19
 
18
20
  __all__ = [
19
21
  "BaseEventHandler",
20
- "CodeAnalysisEventHandler",
22
+ # DISABLED: File Tree interface removed from dashboard
23
+ # "CodeAnalysisEventHandler",
21
24
  "ConnectionEventHandler",
22
25
  "EventHandlerRegistry",
23
26
  "FileEventHandler",
@@ -7,6 +7,7 @@ agent delegations, and other hook-based activity for the system heartbeat.
7
7
  from datetime import datetime, timezone
8
8
  from typing import Any, Dict
9
9
 
10
+ from ....core.enums import ServiceState
10
11
  from .base import BaseEventHandler
11
12
 
12
13
 
@@ -60,6 +61,10 @@ class HookEventHandler(BaseEventHandler):
60
61
 
61
62
  hook_data = data.get("data", {})
62
63
 
64
+ # Log hook event processing
65
+ tool_name = hook_data.get("tool_name", "N/A")
66
+ self.logger.info(f"Processing hook event: {hook_event} - tool: {tool_name}")
67
+
63
68
  # Create properly formatted event for history
64
69
  # Note: add_to_history expects the event data directly, not wrapped
65
70
  history_event = {
@@ -76,6 +81,12 @@ class HookEventHandler(BaseEventHandler):
76
81
 
77
82
  # Broadcast the original event to all connected clients
78
83
  # (preserves all original fields)
84
+ connected_clients = (
85
+ len(self.server.clients) if hasattr(self.server, "clients") else 0
86
+ )
87
+ self.logger.info(
88
+ f"Broadcasting claude_event to {connected_clients} clients: {hook_event}"
89
+ )
79
90
  await self.broadcast_event("claude_event", data)
80
91
 
81
92
  # Track sessions based on hook events
@@ -108,7 +119,7 @@ class HookEventHandler(BaseEventHandler):
108
119
  "session_id": session_id,
109
120
  "start_time": datetime.now(timezone.utc).isoformat(),
110
121
  "agent": agent_type,
111
- "status": "active",
122
+ "status": ServiceState.RUNNING,
112
123
  "prompt": data.get("prompt", "")[:100], # First 100 chars
113
124
  "last_activity": datetime.now(timezone.utc).isoformat(),
114
125
  }
@@ -159,7 +170,7 @@ class HookEventHandler(BaseEventHandler):
159
170
  "session_id": session_id,
160
171
  "start_time": datetime.now(timezone.utc).isoformat(),
161
172
  "agent": "pm", # Default to PM
162
- "status": "active",
173
+ "status": ServiceState.RUNNING,
163
174
  "prompt": data.get("prompt_text", "")[:100],
164
175
  "working_directory": data.get("working_directory", ""),
165
176
  "last_activity": datetime.now(timezone.utc).isoformat(),
@@ -15,7 +15,8 @@ if TYPE_CHECKING:
15
15
 
16
16
  from ..server import SocketIOServer
17
17
 
18
- from .code_analysis import CodeAnalysisEventHandler
18
+ # DISABLED: File Tree interface removed from dashboard
19
+ # from .code_analysis import CodeAnalysisEventHandler
19
20
  from .connection import ConnectionEventHandler
20
21
  from .file import FileEventHandler
21
22
  from .git import GitEventHandler
@@ -38,7 +39,8 @@ class EventHandlerRegistry:
38
39
  HookEventHandler, # Hook events for session tracking
39
40
  GitEventHandler, # Git operations
40
41
  FileEventHandler, # File operations
41
- CodeAnalysisEventHandler, # Code analysis for dashboard
42
+ # DISABLED: File Tree interface removed from dashboard
43
+ # CodeAnalysisEventHandler, # Code analysis for dashboard
42
44
  ProjectEventHandler, # Project management (future)
43
45
  MemoryEventHandler, # Memory management (future)
44
46
  ]
@@ -16,6 +16,8 @@ from collections import deque
16
16
  from datetime import datetime, timezone
17
17
  from typing import Any, Dict, List, Optional, Set
18
18
 
19
+ from ....core.enums import ServiceState
20
+
19
21
  try:
20
22
  import aiohttp
21
23
  import socketio
@@ -265,15 +267,15 @@ class SocketIOServer(SocketIOServiceInterface):
265
267
  except Exception as e:
266
268
  self.logger.error(f"Error during EventBus teardown: {e}")
267
269
 
268
- # Stop code analysis handler
270
+ # Stop event handlers
269
271
  if self.event_registry:
270
- from ..handlers import CodeAnalysisEventHandler, ConnectionEventHandler
271
-
272
- # Stop analysis runner
273
- analysis_handler = self.event_registry.get_handler(CodeAnalysisEventHandler)
274
- if analysis_handler and hasattr(analysis_handler, "cleanup"):
275
- analysis_handler.cleanup()
272
+ from ..handlers import ConnectionEventHandler
276
273
 
274
+ # DISABLED: File Tree interface removed from dashboard
275
+ # Stop analysis runner (code analysis handler is disabled)
276
+ # analysis_handler = self.event_registry.get_handler(CodeAnalysisEventHandler)
277
+ # if analysis_handler and hasattr(analysis_handler, "cleanup"):
278
+ # analysis_handler.cleanup()
277
279
  # Stop health monitoring in connection handler
278
280
  conn_handler = self.event_registry.get_handler(ConnectionEventHandler)
279
281
  if conn_handler and hasattr(conn_handler, "stop_health_monitoring"):
@@ -384,7 +386,7 @@ class SocketIOServer(SocketIOServiceInterface):
384
386
  "session_id": session_id,
385
387
  "start_time": datetime.now(timezone.utc).isoformat(),
386
388
  "agent": "pm", # Default to PM, will be updated if delegated
387
- "status": "active",
389
+ "status": ServiceState.RUNNING,
388
390
  "launch_method": launch_method,
389
391
  "working_dir": working_dir,
390
392
  }
@@ -22,6 +22,7 @@ import tty
22
22
  from typing import Any, Dict, List, Optional
23
23
 
24
24
  from claude_mpm.core.base_service import BaseService
25
+ from claude_mpm.core.enums import OperationResult, ServiceState
25
26
  from claude_mpm.services.core.interfaces import SubprocessLauncherInterface
26
27
 
27
28
 
@@ -62,9 +63,17 @@ class SubprocessLauncherService(BaseService, SubprocessLauncherInterface):
62
63
  try:
63
64
  env = kwargs.get("env", self.prepare_subprocess_environment())
64
65
  self.launch_subprocess_interactive(command, env)
65
- return {"status": "launched", "command": command, "method": "interactive"}
66
+ return {
67
+ "status": OperationResult.SUCCESS,
68
+ "command": command,
69
+ "method": "interactive",
70
+ }
66
71
  except Exception as e:
67
- return {"status": "failed", "error": str(e), "command": command}
72
+ return {
73
+ "status": OperationResult.FAILED,
74
+ "error": str(e),
75
+ "command": command,
76
+ }
68
77
 
69
78
  async def launch_subprocess_async(
70
79
  self, command: List[str], **kwargs
@@ -109,7 +118,7 @@ class SubprocessLauncherService(BaseService, SubprocessLauncherInterface):
109
118
  # For now, return unknown status
110
119
  return {
111
120
  "process_id": process_id,
112
- "status": "unknown",
121
+ "status": OperationResult.UNKNOWN,
113
122
  "message": "Process tracking not implemented",
114
123
  }
115
124
 
@@ -160,7 +169,7 @@ class SubprocessLauncherService(BaseService, SubprocessLauncherInterface):
160
169
  # Notify WebSocket clients
161
170
  if self.websocket_server:
162
171
  self.websocket_server.claude_status_changed(
163
- status="running",
172
+ status=ServiceState.RUNNING,
164
173
  pid=process.pid,
165
174
  message="Claude subprocess started",
166
175
  )
@@ -195,7 +204,7 @@ class SubprocessLauncherService(BaseService, SubprocessLauncherInterface):
195
204
  # Notify WebSocket clients
196
205
  if self.websocket_server:
197
206
  self.websocket_server.claude_status_changed(
198
- status="stopped",
207
+ status=ServiceState.STOPPED,
199
208
  message=f"Claude subprocess exited with code {process.returncode}",
200
209
  )
201
210
 
@@ -14,6 +14,7 @@ import re
14
14
  from pathlib import Path
15
15
  from typing import Any, Dict, List, Optional
16
16
 
17
+ from claude_mpm.core.enums import OperationResult
17
18
  from claude_mpm.core.logging_utils import get_logger
18
19
 
19
20
  from ..strategies import (
@@ -120,7 +121,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
120
121
  return self._analyze_ast(target, options)
121
122
 
122
123
  return {
123
- "status": "error",
124
+ "status": OperationResult.ERROR,
124
125
  "message": f"Unsupported target type: {type(target).__name__}",
125
126
  }
126
127
 
@@ -159,7 +160,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
159
160
  metrics["maintainability_index"] = self._calculate_maintainability(metrics)
160
161
 
161
162
  return {
162
- "status": "success",
163
+ "status": OperationResult.SUCCESS,
163
164
  "type": "file",
164
165
  "path": str(file_path),
165
166
  "metrics": metrics,
@@ -168,7 +169,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
168
169
  except Exception as e:
169
170
  logger.error(f"Error analyzing file {file_path}: {e}")
170
171
  return {
171
- "status": "error",
172
+ "status": OperationResult.ERROR,
172
173
  "path": str(file_path),
173
174
  "error": str(e),
174
175
  }
@@ -178,7 +179,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
178
179
  ) -> Dict[str, Any]:
179
180
  """Analyze all code files in a directory."""
180
181
  results = {
181
- "status": "success",
182
+ "status": OperationResult.SUCCESS,
182
183
  "type": "directory",
183
184
  "path": str(dir_path),
184
185
  "files": [],
@@ -195,7 +196,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
195
196
  total_metrics = {}
196
197
  for file_path in code_files:
197
198
  file_result = self._analyze_file(file_path, options)
198
- if file_result["status"] == "success":
199
+ if file_result["status"] == OperationResult.SUCCESS:
199
200
  results["files"].append(file_result)
200
201
 
201
202
  # Aggregate metrics
@@ -285,7 +286,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
285
286
  )
286
287
 
287
288
  return {
288
- "status": "success",
289
+ "status": OperationResult.SUCCESS,
289
290
  "type": "ast",
290
291
  "metrics": metrics,
291
292
  }
@@ -426,7 +427,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
426
427
  """Extract key metrics from analysis results."""
427
428
  metrics = {}
428
429
 
429
- if analysis_result.get("status") != "success":
430
+ if analysis_result.get("status") != OperationResult.SUCCESS:
430
431
  return metrics
431
432
 
432
433
  # Extract relevant metrics