claude-mpm 4.7.4__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 (308) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +118 -0
  3. claude_mpm/agents/BASE_ENGINEER.md +286 -0
  4. claude_mpm/agents/BASE_PM.md +106 -1
  5. claude_mpm/agents/OUTPUT_STYLE.md +329 -11
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +397 -459
  7. claude_mpm/agents/agent_loader.py +17 -5
  8. claude_mpm/agents/frontmatter_validator.py +284 -253
  9. claude_mpm/agents/templates/README.md +465 -0
  10. claude_mpm/agents/templates/agent-manager.json +4 -1
  11. claude_mpm/agents/templates/agentic-coder-optimizer.json +13 -3
  12. claude_mpm/agents/templates/api_qa.json +11 -2
  13. claude_mpm/agents/templates/circuit_breakers.md +638 -0
  14. claude_mpm/agents/templates/clerk-ops.json +12 -2
  15. claude_mpm/agents/templates/code_analyzer.json +8 -2
  16. claude_mpm/agents/templates/content-agent.json +358 -0
  17. claude_mpm/agents/templates/dart_engineer.json +15 -2
  18. claude_mpm/agents/templates/data_engineer.json +15 -2
  19. claude_mpm/agents/templates/documentation.json +10 -2
  20. claude_mpm/agents/templates/engineer.json +21 -1
  21. claude_mpm/agents/templates/gcp_ops_agent.json +12 -2
  22. claude_mpm/agents/templates/git_file_tracking.md +584 -0
  23. claude_mpm/agents/templates/golang_engineer.json +270 -0
  24. claude_mpm/agents/templates/imagemagick.json +4 -1
  25. claude_mpm/agents/templates/java_engineer.json +346 -0
  26. claude_mpm/agents/templates/local_ops_agent.json +1227 -6
  27. claude_mpm/agents/templates/memory_manager.json +4 -1
  28. claude_mpm/agents/templates/nextjs_engineer.json +141 -133
  29. claude_mpm/agents/templates/ops.json +12 -2
  30. claude_mpm/agents/templates/php-engineer.json +270 -174
  31. claude_mpm/agents/templates/pm_examples.md +474 -0
  32. claude_mpm/agents/templates/pm_red_flags.md +240 -0
  33. claude_mpm/agents/templates/product_owner.json +338 -0
  34. claude_mpm/agents/templates/project_organizer.json +14 -4
  35. claude_mpm/agents/templates/prompt-engineer.json +13 -2
  36. claude_mpm/agents/templates/python_engineer.json +174 -81
  37. claude_mpm/agents/templates/qa.json +11 -2
  38. claude_mpm/agents/templates/react_engineer.json +16 -3
  39. claude_mpm/agents/templates/refactoring_engineer.json +12 -2
  40. claude_mpm/agents/templates/research.json +34 -21
  41. claude_mpm/agents/templates/response_format.md +583 -0
  42. claude_mpm/agents/templates/ruby-engineer.json +129 -192
  43. claude_mpm/agents/templates/rust_engineer.json +270 -0
  44. claude_mpm/agents/templates/security.json +10 -2
  45. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  46. claude_mpm/agents/templates/ticketing.json +10 -2
  47. claude_mpm/agents/templates/typescript_engineer.json +116 -125
  48. claude_mpm/agents/templates/validation_templates.md +312 -0
  49. claude_mpm/agents/templates/vercel_ops_agent.json +12 -2
  50. claude_mpm/agents/templates/version_control.json +12 -2
  51. claude_mpm/agents/templates/web_qa.json +11 -2
  52. claude_mpm/agents/templates/web_ui.json +15 -2
  53. claude_mpm/cli/__init__.py +34 -614
  54. claude_mpm/cli/commands/agent_manager.py +25 -12
  55. claude_mpm/cli/commands/agent_state_manager.py +186 -0
  56. claude_mpm/cli/commands/agents.py +235 -148
  57. claude_mpm/cli/commands/agents_detect.py +380 -0
  58. claude_mpm/cli/commands/agents_recommend.py +309 -0
  59. claude_mpm/cli/commands/aggregate.py +7 -3
  60. claude_mpm/cli/commands/analyze.py +9 -4
  61. claude_mpm/cli/commands/analyze_code.py +7 -2
  62. claude_mpm/cli/commands/auto_configure.py +570 -0
  63. claude_mpm/cli/commands/config.py +47 -13
  64. claude_mpm/cli/commands/configure.py +419 -1571
  65. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  66. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  67. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  68. claude_mpm/cli/commands/configure_models.py +18 -0
  69. claude_mpm/cli/commands/configure_navigation.py +167 -0
  70. claude_mpm/cli/commands/configure_paths.py +104 -0
  71. claude_mpm/cli/commands/configure_persistence.py +254 -0
  72. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  73. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  74. claude_mpm/cli/commands/configure_validators.py +73 -0
  75. claude_mpm/cli/commands/local_deploy.py +537 -0
  76. claude_mpm/cli/commands/memory.py +54 -20
  77. claude_mpm/cli/commands/mpm_init.py +585 -196
  78. claude_mpm/cli/commands/mpm_init_handler.py +37 -3
  79. claude_mpm/cli/commands/search.py +170 -4
  80. claude_mpm/cli/commands/upgrade.py +152 -0
  81. claude_mpm/cli/executor.py +202 -0
  82. claude_mpm/cli/helpers.py +105 -0
  83. claude_mpm/cli/interactive/__init__.py +3 -0
  84. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  85. claude_mpm/cli/parsers/__init__.py +7 -1
  86. claude_mpm/cli/parsers/agents_parser.py +9 -0
  87. claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
  88. claude_mpm/cli/parsers/base_parser.py +110 -3
  89. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  90. claude_mpm/cli/parsers/mpm_init_parser.py +65 -5
  91. claude_mpm/cli/shared/output_formatters.py +28 -19
  92. claude_mpm/cli/startup.py +481 -0
  93. claude_mpm/cli/utils.py +52 -1
  94. claude_mpm/commands/mpm-agents-detect.md +168 -0
  95. claude_mpm/commands/mpm-agents-recommend.md +214 -0
  96. claude_mpm/commands/mpm-agents.md +75 -1
  97. claude_mpm/commands/mpm-auto-configure.md +217 -0
  98. claude_mpm/commands/mpm-help.md +163 -0
  99. claude_mpm/commands/mpm-init.md +148 -3
  100. claude_mpm/commands/mpm-version.md +113 -0
  101. claude_mpm/commands/mpm.md +1 -0
  102. claude_mpm/config/agent_config.py +2 -2
  103. claude_mpm/config/model_config.py +428 -0
  104. claude_mpm/constants.py +1 -0
  105. claude_mpm/core/base_service.py +13 -12
  106. claude_mpm/core/enums.py +452 -0
  107. claude_mpm/core/factories.py +1 -1
  108. claude_mpm/core/instruction_reinforcement_hook.py +2 -1
  109. claude_mpm/core/interactive_session.py +9 -3
  110. claude_mpm/core/log_manager.py +2 -0
  111. claude_mpm/core/logging_config.py +6 -2
  112. claude_mpm/core/oneshot_session.py +8 -4
  113. claude_mpm/core/optimized_agent_loader.py +3 -3
  114. claude_mpm/core/output_style_manager.py +12 -192
  115. claude_mpm/core/service_registry.py +5 -1
  116. claude_mpm/core/types.py +2 -9
  117. claude_mpm/core/typing_utils.py +7 -6
  118. claude_mpm/dashboard/static/js/dashboard.js +0 -14
  119. claude_mpm/dashboard/templates/index.html +3 -41
  120. claude_mpm/hooks/__init__.py +20 -0
  121. claude_mpm/hooks/claude_hooks/event_handlers.py +4 -2
  122. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  123. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +23 -2
  124. claude_mpm/hooks/failure_learning/__init__.py +60 -0
  125. claude_mpm/hooks/failure_learning/failure_detection_hook.py +235 -0
  126. claude_mpm/hooks/failure_learning/fix_detection_hook.py +217 -0
  127. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +286 -0
  128. claude_mpm/hooks/instruction_reinforcement.py +7 -2
  129. claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
  130. claude_mpm/hooks/kuzu_memory_hook.py +37 -12
  131. claude_mpm/hooks/kuzu_response_hook.py +183 -0
  132. claude_mpm/models/resume_log.py +340 -0
  133. claude_mpm/services/agents/__init__.py +18 -5
  134. claude_mpm/services/agents/auto_config_manager.py +796 -0
  135. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  136. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  137. claude_mpm/services/agents/deployment/agent_validator.py +17 -1
  138. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  139. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  140. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  141. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
  142. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  143. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  144. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
  145. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  146. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
  147. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  148. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  149. claude_mpm/services/agents/local_template_manager.py +1 -1
  150. claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
  151. claude_mpm/services/agents/observers.py +547 -0
  152. claude_mpm/services/agents/recommender.py +568 -0
  153. claude_mpm/services/agents/registry/modification_tracker.py +5 -2
  154. claude_mpm/services/command_handler_service.py +11 -5
  155. claude_mpm/services/core/__init__.py +33 -1
  156. claude_mpm/services/core/interfaces/__init__.py +90 -3
  157. claude_mpm/services/core/interfaces/agent.py +184 -0
  158. claude_mpm/services/core/interfaces/health.py +172 -0
  159. claude_mpm/services/core/interfaces/model.py +281 -0
  160. claude_mpm/services/core/interfaces/process.py +372 -0
  161. claude_mpm/services/core/interfaces/project.py +121 -0
  162. claude_mpm/services/core/interfaces/restart.py +307 -0
  163. claude_mpm/services/core/interfaces/stability.py +260 -0
  164. claude_mpm/services/core/memory_manager.py +11 -24
  165. claude_mpm/services/core/models/__init__.py +79 -0
  166. claude_mpm/services/core/models/agent_config.py +381 -0
  167. claude_mpm/services/core/models/health.py +162 -0
  168. claude_mpm/services/core/models/process.py +235 -0
  169. claude_mpm/services/core/models/restart.py +302 -0
  170. claude_mpm/services/core/models/stability.py +264 -0
  171. claude_mpm/services/core/models/toolchain.py +306 -0
  172. claude_mpm/services/core/path_resolver.py +23 -7
  173. claude_mpm/services/diagnostics/__init__.py +2 -2
  174. claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
  175. claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
  176. claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
  177. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
  178. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  179. claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
  180. claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
  181. claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
  182. claude_mpm/services/diagnostics/checks/mcp_services_check.py +38 -33
  183. claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
  184. claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
  185. claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
  186. claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
  187. claude_mpm/services/diagnostics/models.py +19 -24
  188. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
  189. claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
  190. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  191. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  192. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  193. claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
  194. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  195. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  196. claude_mpm/services/local_ops/__init__.py +163 -0
  197. claude_mpm/services/local_ops/crash_detector.py +257 -0
  198. claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
  199. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  200. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  201. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  202. claude_mpm/services/local_ops/health_manager.py +430 -0
  203. claude_mpm/services/local_ops/log_monitor.py +396 -0
  204. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  205. claude_mpm/services/local_ops/process_manager.py +595 -0
  206. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  207. claude_mpm/services/local_ops/restart_manager.py +401 -0
  208. claude_mpm/services/local_ops/restart_policy.py +387 -0
  209. claude_mpm/services/local_ops/state_manager.py +372 -0
  210. claude_mpm/services/local_ops/unified_manager.py +600 -0
  211. claude_mpm/services/mcp_config_manager.py +9 -4
  212. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  213. claude_mpm/services/mcp_gateway/core/base.py +18 -31
  214. claude_mpm/services/mcp_gateway/main.py +30 -0
  215. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +206 -32
  216. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
  217. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +25 -5
  218. claude_mpm/services/mcp_service_verifier.py +1 -1
  219. claude_mpm/services/memory/failure_tracker.py +563 -0
  220. claude_mpm/services/memory_hook_service.py +165 -4
  221. claude_mpm/services/model/__init__.py +147 -0
  222. claude_mpm/services/model/base_provider.py +365 -0
  223. claude_mpm/services/model/claude_provider.py +412 -0
  224. claude_mpm/services/model/model_router.py +453 -0
  225. claude_mpm/services/model/ollama_provider.py +415 -0
  226. claude_mpm/services/monitor/daemon_manager.py +3 -2
  227. claude_mpm/services/monitor/handlers/dashboard.py +2 -1
  228. claude_mpm/services/monitor/handlers/hooks.py +2 -1
  229. claude_mpm/services/monitor/management/lifecycle.py +3 -2
  230. claude_mpm/services/monitor/server.py +2 -1
  231. claude_mpm/services/project/__init__.py +23 -0
  232. claude_mpm/services/project/detection_strategies.py +719 -0
  233. claude_mpm/services/project/toolchain_analyzer.py +581 -0
  234. claude_mpm/services/self_upgrade_service.py +342 -0
  235. claude_mpm/services/session_management_service.py +3 -2
  236. claude_mpm/services/session_manager.py +205 -1
  237. claude_mpm/services/shared/async_service_base.py +16 -27
  238. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  239. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  240. claude_mpm/services/socketio/handlers/hook.py +13 -2
  241. claude_mpm/services/socketio/handlers/registry.py +4 -2
  242. claude_mpm/services/socketio/server/main.py +10 -8
  243. claude_mpm/services/subprocess_launcher_service.py +14 -5
  244. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
  245. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
  246. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
  247. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
  248. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
  249. claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
  250. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
  251. claude_mpm/services/unified/deployment_strategies/local.py +6 -5
  252. claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
  253. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
  254. claude_mpm/services/unified/interfaces.py +3 -1
  255. claude_mpm/services/unified/unified_analyzer.py +14 -10
  256. claude_mpm/services/unified/unified_config.py +2 -1
  257. claude_mpm/services/unified/unified_deployment.py +9 -4
  258. claude_mpm/services/version_service.py +104 -1
  259. claude_mpm/skills/__init__.py +21 -0
  260. claude_mpm/skills/bundled/__init__.py +6 -0
  261. claude_mpm/skills/bundled/api-documentation.md +393 -0
  262. claude_mpm/skills/bundled/async-testing.md +571 -0
  263. claude_mpm/skills/bundled/code-review.md +143 -0
  264. claude_mpm/skills/bundled/database-migration.md +199 -0
  265. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  266. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  267. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  268. claude_mpm/skills/bundled/git-workflow.md +414 -0
  269. claude_mpm/skills/bundled/imagemagick.md +204 -0
  270. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  271. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  272. claude_mpm/skills/bundled/pdf.md +141 -0
  273. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  274. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  275. claude_mpm/skills/bundled/security-scanning.md +327 -0
  276. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  277. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  278. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  279. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  280. claude_mpm/skills/bundled/xlsx.md +157 -0
  281. claude_mpm/skills/registry.py +286 -0
  282. claude_mpm/skills/skill_manager.py +310 -0
  283. claude_mpm/storage/state_storage.py +15 -15
  284. claude_mpm/tools/code_tree_analyzer.py +177 -141
  285. claude_mpm/tools/code_tree_events.py +4 -2
  286. claude_mpm/utils/agent_dependency_loader.py +40 -20
  287. claude_mpm/utils/display_helper.py +260 -0
  288. claude_mpm/utils/git_analyzer.py +407 -0
  289. claude_mpm/utils/robust_installer.py +73 -19
  290. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +129 -12
  291. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +295 -193
  292. claude_mpm/dashboard/static/css/code-tree.css +0 -1639
  293. claude_mpm/dashboard/static/index-hub-backup.html +0 -713
  294. claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
  295. claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
  296. claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
  297. claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
  298. claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
  299. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
  300. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  301. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
  302. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  303. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  304. claude_mpm/services/project/analyzer_refactored.py +0 -450
  305. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
  306. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
  307. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
  308. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,8 @@ Monitors service-specific metrics like client connections, event processing, and
6
6
  import time
7
7
  from typing import Any, Dict, List
8
8
 
9
- from .base import BaseMonitoringService, HealthMetric, HealthStatus
9
+ from ....core.enums import HealthStatus
10
+ from .base import BaseMonitoringService, HealthMetric
10
11
 
11
12
 
12
13
  class ServiceHealthService(BaseMonitoringService):
@@ -79,9 +80,9 @@ class ServiceHealthService(BaseMonitoringService):
79
80
 
80
81
  # Determine status based on thresholds
81
82
  if client_count > self.max_clients:
82
- client_status = HealthStatus.CRITICAL
83
+ client_status = HealthStatus.UNHEALTHY
83
84
  elif client_count > self.max_clients * 0.8:
84
- client_status = HealthStatus.WARNING
85
+ client_status = HealthStatus.DEGRADED
85
86
  else:
86
87
  client_status = HealthStatus.HEALTHY
87
88
 
@@ -129,7 +130,7 @@ class ServiceHealthService(BaseMonitoringService):
129
130
  # Determine status based on rate
130
131
  rate_status = HealthStatus.HEALTHY
131
132
  if event_rate == 0 and events_processed > 0:
132
- rate_status = HealthStatus.WARNING # Processing stopped
133
+ rate_status = HealthStatus.DEGRADED # Processing stopped
133
134
 
134
135
  metrics.append(
135
136
  HealthMetric(
@@ -157,9 +158,9 @@ class ServiceHealthService(BaseMonitoringService):
157
158
  queue_size = self.service_stats["event_queue_size"]
158
159
  queue_status = HealthStatus.HEALTHY
159
160
  if queue_size > 1000:
160
- queue_status = HealthStatus.WARNING
161
+ queue_status = HealthStatus.DEGRADED
161
162
  if queue_size > 5000:
162
- queue_status = HealthStatus.CRITICAL
163
+ queue_status = HealthStatus.UNHEALTHY
163
164
 
164
165
  metrics.append(
165
166
  HealthMetric(
@@ -191,9 +192,9 @@ class ServiceHealthService(BaseMonitoringService):
191
192
 
192
193
  # Determine status based on rate
193
194
  if error_rate > self.max_error_rate:
194
- error_status = HealthStatus.CRITICAL
195
+ error_status = HealthStatus.UNHEALTHY
195
196
  elif error_rate > self.max_error_rate * 0.5:
196
- error_status = HealthStatus.WARNING
197
+ error_status = HealthStatus.DEGRADED
197
198
  else:
198
199
  error_status = HealthStatus.HEALTHY
199
200
 
@@ -213,7 +214,7 @@ class ServiceHealthService(BaseMonitoringService):
213
214
  name="total_errors",
214
215
  value=errors,
215
216
  status=(
216
- HealthStatus.HEALTHY if errors == 0 else HealthStatus.WARNING
217
+ HealthStatus.HEALTHY if errors == 0 else HealthStatus.DEGRADED
217
218
  ),
218
219
  )
219
220
  )
@@ -228,7 +229,7 @@ class ServiceHealthService(BaseMonitoringService):
228
229
  status=(
229
230
  HealthStatus.HEALTHY
230
231
  if recent_errors == 0
231
- else HealthStatus.WARNING
232
+ else HealthStatus.DEGRADED
232
233
  ),
233
234
  )
234
235
  )
@@ -263,9 +264,9 @@ class ServiceHealthService(BaseMonitoringService):
263
264
 
264
265
  # Determine status based on staleness
265
266
  if time_since_activity > self.stale_activity_seconds * 2:
266
- activity_status = HealthStatus.CRITICAL
267
+ activity_status = HealthStatus.UNHEALTHY
267
268
  elif time_since_activity > self.stale_activity_seconds:
268
- activity_status = HealthStatus.WARNING
269
+ activity_status = HealthStatus.DEGRADED
269
270
  else:
270
271
  activity_status = HealthStatus.HEALTHY
271
272
 
@@ -282,7 +283,7 @@ class ServiceHealthService(BaseMonitoringService):
282
283
  HealthMetric(
283
284
  name="time_since_last_activity",
284
285
  value=-1,
285
- status=HealthStatus.WARNING,
286
+ status=HealthStatus.DEGRADED,
286
287
  message="No last activity recorded",
287
288
  )
288
289
  )
@@ -307,9 +308,9 @@ class ServiceHealthService(BaseMonitoringService):
307
308
 
308
309
  # Determine status based on response time
309
310
  if avg_time > 1000: # > 1 second
310
- time_status = HealthStatus.CRITICAL
311
+ time_status = HealthStatus.UNHEALTHY
311
312
  elif avg_time > 500: # > 500ms
312
- time_status = HealthStatus.WARNING
313
+ time_status = HealthStatus.DEGRADED
313
314
  else:
314
315
  time_status = HealthStatus.HEALTHY
315
316
 
@@ -0,0 +1,439 @@
1
+ """Resume Log Generator Service.
2
+
3
+ Automatically generates session resume logs when approaching or hitting token limits.
4
+ Integrates with session management and response tracking infrastructure.
5
+
6
+ Triggers:
7
+ - model_context_window_exceeded (stop_reason)
8
+ - Manual pause command
9
+ - 95% token threshold reached
10
+ - Session end with high token usage (>85%)
11
+
12
+ Design Principles:
13
+ - Atomic file operations (via state_storage)
14
+ - Non-blocking generation
15
+ - Graceful degradation if generation fails
16
+ - Integration with existing session state
17
+ """
18
+
19
+ from datetime import datetime, timezone
20
+ from pathlib import Path
21
+ from typing import Any, Dict, List, Optional
22
+
23
+ from claude_mpm.core.logging_utils import get_logger
24
+ from claude_mpm.models.resume_log import ContextMetrics, ResumeLog
25
+ from claude_mpm.storage.state_storage import StateStorage
26
+
27
+ logger = get_logger(__name__)
28
+
29
+
30
+ class ResumeLogGenerator:
31
+ """Service for generating session resume logs."""
32
+
33
+ def __init__(
34
+ self,
35
+ storage_dir: Optional[Path] = None,
36
+ config: Optional[Dict[str, Any]] = None,
37
+ ):
38
+ """Initialize resume log generator.
39
+
40
+ Args:
41
+ storage_dir: Directory for resume logs (default: .claude-mpm/resume-logs)
42
+ config: Configuration dictionary
43
+ """
44
+ self.storage_dir = storage_dir or Path.home() / ".claude-mpm" / "resume-logs"
45
+ self.storage_dir.mkdir(parents=True, exist_ok=True)
46
+
47
+ # State storage for atomic writes
48
+ self.state_storage = StateStorage(
49
+ storage_dir=self.storage_dir.parent / "storage"
50
+ )
51
+
52
+ # Configuration
53
+ self.config = config or {}
54
+ self.enabled = (
55
+ self.config.get("context_management", {})
56
+ .get("resume_logs", {})
57
+ .get("enabled", True)
58
+ )
59
+ self.auto_generate = (
60
+ self.config.get("context_management", {})
61
+ .get("resume_logs", {})
62
+ .get("auto_generate", True)
63
+ )
64
+ self.max_tokens = (
65
+ self.config.get("context_management", {})
66
+ .get("resume_logs", {})
67
+ .get("max_tokens", 10000)
68
+ )
69
+
70
+ # Trigger thresholds
71
+ thresholds = self.config.get("context_management", {}).get("thresholds", {})
72
+ self.threshold_caution = thresholds.get("caution", 0.70)
73
+ self.threshold_warning = thresholds.get("warning", 0.85)
74
+ self.threshold_critical = thresholds.get("critical", 0.95)
75
+
76
+ logger.info(
77
+ f"ResumeLogGenerator initialized (enabled={self.enabled}, auto_generate={self.auto_generate})"
78
+ )
79
+
80
+ def should_generate(
81
+ self,
82
+ stop_reason: Optional[str] = None,
83
+ token_usage_pct: Optional[float] = None,
84
+ manual_trigger: bool = False,
85
+ ) -> bool:
86
+ """Determine if resume log should be generated.
87
+
88
+ Args:
89
+ stop_reason: Claude API stop_reason
90
+ token_usage_pct: Current token usage percentage (0.0-1.0)
91
+ manual_trigger: Manual pause/stop command
92
+
93
+ Returns:
94
+ True if resume log should be generated
95
+ """
96
+ if not self.enabled or not self.auto_generate:
97
+ return manual_trigger # Only generate on manual trigger if auto is disabled
98
+
99
+ # Trigger conditions
100
+ triggers = [
101
+ stop_reason == "max_tokens",
102
+ stop_reason == "model_context_window_exceeded",
103
+ manual_trigger,
104
+ token_usage_pct and token_usage_pct >= self.threshold_critical,
105
+ token_usage_pct
106
+ and token_usage_pct >= self.threshold_warning, # Generate at 85% too
107
+ ]
108
+
109
+ should_gen = any(triggers)
110
+
111
+ if should_gen:
112
+ reason = "unknown"
113
+ if stop_reason:
114
+ reason = f"stop_reason={stop_reason}"
115
+ elif manual_trigger:
116
+ reason = "manual_trigger"
117
+ elif token_usage_pct:
118
+ reason = f"token_usage={token_usage_pct:.1%}"
119
+
120
+ logger.info(f"Resume log generation triggered: {reason}")
121
+
122
+ return should_gen
123
+
124
+ def generate_from_session_state(
125
+ self,
126
+ session_id: str,
127
+ session_state: Dict[str, Any],
128
+ stop_reason: Optional[str] = None,
129
+ ) -> Optional[ResumeLog]:
130
+ """Generate resume log from session state data.
131
+
132
+ Args:
133
+ session_id: Current session ID
134
+ session_state: Session state dictionary
135
+ stop_reason: Claude API stop_reason
136
+
137
+ Returns:
138
+ Generated ResumeLog or None if generation failed
139
+ """
140
+ try:
141
+ # Extract context metrics
142
+ context_data = session_state.get("context_metrics", {})
143
+ context_metrics = ContextMetrics(
144
+ total_budget=context_data.get("total_budget", 200000),
145
+ used_tokens=context_data.get("used_tokens", 0),
146
+ remaining_tokens=context_data.get("remaining_tokens", 0),
147
+ percentage_used=context_data.get("percentage_used", 0.0),
148
+ stop_reason=stop_reason or context_data.get("stop_reason"),
149
+ model=context_data.get("model", "claude-sonnet-4.5"),
150
+ session_id=session_id,
151
+ )
152
+
153
+ # Extract content from session state
154
+ mission_summary = session_state.get("mission_summary", "")
155
+ accomplishments = session_state.get("accomplishments", [])
156
+ key_findings = session_state.get("key_findings", [])
157
+ decisions_made = session_state.get("decisions_made", [])
158
+ next_steps = session_state.get("next_steps", [])
159
+ critical_context = session_state.get("critical_context", {})
160
+
161
+ # Extract metadata
162
+ files_modified = session_state.get("files_modified", [])
163
+ agents_used = session_state.get("agents_used", {})
164
+ errors_encountered = session_state.get("errors_encountered", [])
165
+ warnings = session_state.get("warnings", [])
166
+
167
+ # Create resume log
168
+ resume_log = ResumeLog(
169
+ session_id=session_id,
170
+ previous_session_id=session_state.get("previous_session_id"),
171
+ context_metrics=context_metrics,
172
+ mission_summary=mission_summary,
173
+ accomplishments=accomplishments,
174
+ key_findings=key_findings,
175
+ decisions_made=decisions_made,
176
+ next_steps=next_steps,
177
+ critical_context=critical_context,
178
+ files_modified=files_modified,
179
+ agents_used=agents_used,
180
+ errors_encountered=errors_encountered,
181
+ warnings=warnings,
182
+ )
183
+
184
+ logger.info(f"Generated resume log for session {session_id}")
185
+ return resume_log
186
+
187
+ except Exception as e:
188
+ logger.error(
189
+ f"Failed to generate resume log from session state: {e}", exc_info=True
190
+ )
191
+ return None
192
+
193
+ def generate_from_todo_list(
194
+ self,
195
+ session_id: str,
196
+ todos: List[Dict[str, Any]],
197
+ context_metrics: Optional[ContextMetrics] = None,
198
+ ) -> Optional[ResumeLog]:
199
+ """Generate resume log from TODO list.
200
+
201
+ Useful when session state is minimal but TODO list has rich information.
202
+
203
+ Args:
204
+ session_id: Current session ID
205
+ todos: TODO list items
206
+ context_metrics: Context metrics (optional)
207
+
208
+ Returns:
209
+ Generated ResumeLog or None if generation failed
210
+ """
211
+ try:
212
+ # Categorize todos
213
+ completed = [t for t in todos if t.get("status") == "completed"]
214
+ in_progress = [t for t in todos if t.get("status") == "in_progress"]
215
+ pending = [t for t in todos if t.get("status") == "pending"]
216
+
217
+ # Build accomplishments from completed tasks
218
+ accomplishments = [f"✓ {task['content']}" for task in completed]
219
+
220
+ # Build next steps from in-progress and pending
221
+ next_steps = []
222
+ for task in in_progress:
223
+ next_steps.append(f"[IN PROGRESS] {task['content']}")
224
+ for task in pending:
225
+ next_steps.append(f"[PENDING] {task['content']}")
226
+
227
+ # Create mission summary
228
+ mission_summary = f"Working on {len(todos)} tasks: {len(completed)} completed, {len(in_progress)} in progress, {len(pending)} pending."
229
+
230
+ # Use provided context metrics or create default
231
+ if context_metrics is None:
232
+ context_metrics = ContextMetrics(session_id=session_id)
233
+
234
+ # Create resume log
235
+ resume_log = ResumeLog(
236
+ session_id=session_id,
237
+ context_metrics=context_metrics,
238
+ mission_summary=mission_summary,
239
+ accomplishments=accomplishments,
240
+ next_steps=next_steps,
241
+ critical_context={
242
+ "total_tasks": len(todos),
243
+ "completed_tasks": len(completed),
244
+ "in_progress_tasks": len(in_progress),
245
+ "pending_tasks": len(pending),
246
+ },
247
+ )
248
+
249
+ logger.info(f"Generated resume log from TODO list for session {session_id}")
250
+ return resume_log
251
+
252
+ except Exception as e:
253
+ logger.error(
254
+ f"Failed to generate resume log from TODO list: {e}", exc_info=True
255
+ )
256
+ return None
257
+
258
+ def save_resume_log(self, resume_log: ResumeLog) -> Optional[Path]:
259
+ """Save resume log to storage.
260
+
261
+ Args:
262
+ resume_log: ResumeLog instance to save
263
+
264
+ Returns:
265
+ Path to saved file or None if save failed
266
+ """
267
+ try:
268
+ # Save as markdown (primary format)
269
+ md_path = resume_log.save(storage_dir=self.storage_dir)
270
+
271
+ # Also save as JSON for programmatic access
272
+ json_path = self.storage_dir / f"session-{resume_log.session_id}.json"
273
+ self.state_storage.write_json(
274
+ data=resume_log.to_dict(),
275
+ file_path=json_path,
276
+ atomic=True,
277
+ )
278
+
279
+ logger.info(f"Resume log saved: {md_path}")
280
+ return md_path
281
+
282
+ except Exception as e:
283
+ logger.error(f"Failed to save resume log: {e}", exc_info=True)
284
+ return None
285
+
286
+ def load_resume_log(self, session_id: str) -> Optional[str]:
287
+ """Load resume log markdown content.
288
+
289
+ Args:
290
+ session_id: Session ID to load
291
+
292
+ Returns:
293
+ Markdown content or None if not found
294
+ """
295
+ try:
296
+ md_path = self.storage_dir / f"session-{session_id}.md"
297
+
298
+ if not md_path.exists():
299
+ logger.debug(f"Resume log not found for session {session_id}")
300
+ return None
301
+
302
+ content = md_path.read_text(encoding="utf-8")
303
+ logger.info(f"Loaded resume log for session {session_id}")
304
+ return content
305
+
306
+ except Exception as e:
307
+ logger.error(f"Failed to load resume log: {e}", exc_info=True)
308
+ return None
309
+
310
+ def list_resume_logs(self) -> List[Dict[str, Any]]:
311
+ """List all available resume logs.
312
+
313
+ Returns:
314
+ List of resume log metadata
315
+ """
316
+ try:
317
+ logs = []
318
+
319
+ for md_file in self.storage_dir.glob("session-*.md"):
320
+ # Extract session ID from filename
321
+ session_id = md_file.stem.replace("session-", "")
322
+
323
+ # Check if JSON metadata exists
324
+ json_file = md_file.with_suffix(".json")
325
+ metadata = {}
326
+ if json_file.exists():
327
+ json_data = self.state_storage.read_json(json_file)
328
+ if json_data:
329
+ metadata = {
330
+ "session_id": session_id,
331
+ "created_at": json_data.get("created_at"),
332
+ "previous_session_id": json_data.get("previous_session_id"),
333
+ "context_metrics": json_data.get("context_metrics", {}),
334
+ "file_path": str(md_file),
335
+ }
336
+
337
+ if metadata:
338
+ logs.append(metadata)
339
+ else:
340
+ # Fallback to file metadata
341
+ logs.append(
342
+ {
343
+ "session_id": session_id,
344
+ "file_path": str(md_file),
345
+ "modified_at": datetime.fromtimestamp(
346
+ md_file.stat().st_mtime, tz=timezone.utc
347
+ ).isoformat(),
348
+ }
349
+ )
350
+
351
+ # Sort by creation time (newest first)
352
+ logs.sort(
353
+ key=lambda x: x.get("created_at", x.get("modified_at", "")),
354
+ reverse=True,
355
+ )
356
+
357
+ logger.debug(f"Found {len(logs)} resume logs")
358
+ return logs
359
+
360
+ except Exception as e:
361
+ logger.error(f"Failed to list resume logs: {e}", exc_info=True)
362
+ return []
363
+
364
+ def cleanup_old_logs(self, keep_count: int = 10) -> int:
365
+ """Clean up old resume logs, keeping only the most recent.
366
+
367
+ Args:
368
+ keep_count: Number of logs to keep
369
+
370
+ Returns:
371
+ Number of logs deleted
372
+ """
373
+ try:
374
+ logs = self.list_resume_logs()
375
+
376
+ if len(logs) <= keep_count:
377
+ logger.debug(
378
+ f"No cleanup needed ({len(logs)} logs <= {keep_count} keep)"
379
+ )
380
+ return 0
381
+
382
+ # Delete old logs
383
+ deleted = 0
384
+ for log in logs[keep_count:]:
385
+ try:
386
+ md_path = Path(log["file_path"])
387
+ json_path = md_path.with_suffix(".json")
388
+
389
+ if md_path.exists():
390
+ md_path.unlink()
391
+ deleted += 1
392
+
393
+ if json_path.exists():
394
+ json_path.unlink()
395
+
396
+ except Exception as e:
397
+ logger.warning(f"Failed to delete log {log['session_id']}: {e}")
398
+
399
+ logger.info(f"Cleaned up {deleted} old resume logs (kept {keep_count})")
400
+ return deleted
401
+
402
+ except Exception as e:
403
+ logger.error(f"Failed to cleanup old logs: {e}", exc_info=True)
404
+ return 0
405
+
406
+ def get_stats(self) -> Dict[str, Any]:
407
+ """Get resume log statistics.
408
+
409
+ Returns:
410
+ Dictionary with statistics
411
+ """
412
+ try:
413
+ logs = self.list_resume_logs()
414
+
415
+ total_size = 0
416
+ for log in logs:
417
+ path = Path(log["file_path"])
418
+ if path.exists():
419
+ total_size += path.stat().st_size
420
+
421
+ return {
422
+ "enabled": self.enabled,
423
+ "auto_generate": self.auto_generate,
424
+ "total_logs": len(logs),
425
+ "storage_dir": str(self.storage_dir),
426
+ "total_size_kb": round(total_size / 1024, 2),
427
+ "thresholds": {
428
+ "caution": f"{self.threshold_caution:.0%}",
429
+ "warning": f"{self.threshold_warning:.0%}",
430
+ "critical": f"{self.threshold_critical:.0%}",
431
+ },
432
+ }
433
+
434
+ except Exception as e:
435
+ logger.error(f"Failed to get stats: {e}", exc_info=True)
436
+ return {
437
+ "enabled": self.enabled,
438
+ "error": str(e),
439
+ }