claude-mpm 4.1.1__py3-none-any.whl → 4.1.3__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 (389) hide show
  1. claude_mpm/BUILD_NUMBER +1 -1
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/__main__.py +1 -1
  4. claude_mpm/agents/BASE_PM.md +74 -46
  5. claude_mpm/agents/INSTRUCTIONS.md +11 -153
  6. claude_mpm/agents/WORKFLOW.md +61 -321
  7. claude_mpm/agents/__init__.py +11 -11
  8. claude_mpm/agents/agent_loader.py +23 -20
  9. claude_mpm/agents/agent_loader_integration.py +1 -1
  10. claude_mpm/agents/agents_metadata.py +27 -0
  11. claude_mpm/agents/async_agent_loader.py +5 -8
  12. claude_mpm/agents/base_agent_loader.py +36 -25
  13. claude_mpm/agents/frontmatter_validator.py +6 -6
  14. claude_mpm/agents/schema/agent_schema.json +1 -1
  15. claude_mpm/agents/system_agent_config.py +9 -9
  16. claude_mpm/agents/templates/api_qa.json +47 -2
  17. claude_mpm/agents/templates/engineer.json +33 -11
  18. claude_mpm/agents/templates/imagemagick.json +256 -0
  19. claude_mpm/agents/templates/qa.json +41 -2
  20. claude_mpm/agents/templates/ticketing.json +5 -5
  21. claude_mpm/agents/templates/web_qa.json +50 -2
  22. claude_mpm/cli/__init__.py +51 -46
  23. claude_mpm/cli/__main__.py +1 -1
  24. claude_mpm/cli/commands/__init__.py +10 -12
  25. claude_mpm/cli/commands/agent_manager.py +186 -181
  26. claude_mpm/cli/commands/agents.py +648 -1098
  27. claude_mpm/cli/commands/aggregate.py +30 -29
  28. claude_mpm/cli/commands/cleanup.py +50 -44
  29. claude_mpm/cli/commands/cleanup_orphaned_agents.py +25 -25
  30. claude_mpm/cli/commands/config.py +162 -127
  31. claude_mpm/cli/commands/doctor.py +52 -62
  32. claude_mpm/cli/commands/info.py +37 -25
  33. claude_mpm/cli/commands/mcp.py +3 -7
  34. claude_mpm/cli/commands/mcp_command_router.py +14 -18
  35. claude_mpm/cli/commands/mcp_install_commands.py +28 -23
  36. claude_mpm/cli/commands/mcp_pipx_config.py +58 -49
  37. claude_mpm/cli/commands/mcp_server_commands.py +23 -17
  38. claude_mpm/cli/commands/memory.py +339 -967
  39. claude_mpm/cli/commands/monitor.py +117 -88
  40. claude_mpm/cli/commands/run.py +233 -542
  41. claude_mpm/cli/commands/socketio_monitor.py +17 -19
  42. claude_mpm/cli/commands/tickets.py +92 -92
  43. claude_mpm/cli/parser.py +1 -5
  44. claude_mpm/cli/parsers/__init__.py +1 -1
  45. claude_mpm/cli/parsers/agent_manager_parser.py +50 -98
  46. claude_mpm/cli/parsers/agents_parser.py +2 -3
  47. claude_mpm/cli/parsers/base_parser.py +7 -5
  48. claude_mpm/cli/parsers/mcp_parser.py +4 -2
  49. claude_mpm/cli/parsers/monitor_parser.py +26 -18
  50. claude_mpm/cli/shared/__init__.py +10 -10
  51. claude_mpm/cli/shared/argument_patterns.py +57 -71
  52. claude_mpm/cli/shared/base_command.py +61 -53
  53. claude_mpm/cli/shared/error_handling.py +62 -58
  54. claude_mpm/cli/shared/output_formatters.py +78 -77
  55. claude_mpm/cli/startup_logging.py +280 -172
  56. claude_mpm/cli/utils.py +10 -11
  57. claude_mpm/cli_module/__init__.py +1 -1
  58. claude_mpm/cli_module/args.py +1 -1
  59. claude_mpm/cli_module/migration_example.py +5 -5
  60. claude_mpm/config/__init__.py +9 -9
  61. claude_mpm/config/agent_config.py +15 -14
  62. claude_mpm/config/experimental_features.py +4 -4
  63. claude_mpm/config/paths.py +0 -1
  64. claude_mpm/config/socketio_config.py +5 -6
  65. claude_mpm/constants.py +1 -2
  66. claude_mpm/core/__init__.py +8 -8
  67. claude_mpm/core/agent_name_normalizer.py +1 -1
  68. claude_mpm/core/agent_registry.py +22 -29
  69. claude_mpm/core/agent_session_manager.py +3 -3
  70. claude_mpm/core/base_service.py +7 -15
  71. claude_mpm/core/cache.py +4 -6
  72. claude_mpm/core/claude_runner.py +85 -113
  73. claude_mpm/core/config.py +43 -28
  74. claude_mpm/core/config_aliases.py +0 -9
  75. claude_mpm/core/config_constants.py +52 -30
  76. claude_mpm/core/constants.py +0 -1
  77. claude_mpm/core/container.py +18 -27
  78. claude_mpm/core/exceptions.py +2 -2
  79. claude_mpm/core/factories.py +10 -12
  80. claude_mpm/core/framework_loader.py +500 -680
  81. claude_mpm/core/hook_manager.py +26 -22
  82. claude_mpm/core/hook_performance_config.py +58 -47
  83. claude_mpm/core/injectable_service.py +1 -1
  84. claude_mpm/core/interactive_session.py +61 -152
  85. claude_mpm/core/interfaces.py +1 -100
  86. claude_mpm/core/lazy.py +5 -5
  87. claude_mpm/core/log_manager.py +587 -0
  88. claude_mpm/core/logger.py +125 -8
  89. claude_mpm/core/logging_config.py +15 -17
  90. claude_mpm/core/minimal_framework_loader.py +5 -8
  91. claude_mpm/core/oneshot_session.py +15 -33
  92. claude_mpm/core/optimized_agent_loader.py +4 -6
  93. claude_mpm/core/optimized_startup.py +2 -1
  94. claude_mpm/core/output_style_manager.py +147 -106
  95. claude_mpm/core/pm_hook_interceptor.py +0 -1
  96. claude_mpm/core/service_registry.py +11 -8
  97. claude_mpm/core/session_manager.py +1 -2
  98. claude_mpm/core/shared/__init__.py +1 -1
  99. claude_mpm/core/shared/config_loader.py +101 -97
  100. claude_mpm/core/shared/path_resolver.py +72 -68
  101. claude_mpm/core/shared/singleton_manager.py +56 -50
  102. claude_mpm/core/socketio_pool.py +26 -6
  103. claude_mpm/core/tool_access_control.py +4 -5
  104. claude_mpm/core/typing_utils.py +50 -59
  105. claude_mpm/core/unified_agent_registry.py +14 -19
  106. claude_mpm/core/unified_config.py +4 -6
  107. claude_mpm/core/unified_paths.py +197 -109
  108. claude_mpm/dashboard/open_dashboard.py +2 -4
  109. claude_mpm/experimental/cli_enhancements.py +51 -36
  110. claude_mpm/generators/agent_profile_generator.py +2 -4
  111. claude_mpm/hooks/base_hook.py +1 -2
  112. claude_mpm/hooks/claude_hooks/connection_pool.py +72 -26
  113. claude_mpm/hooks/claude_hooks/event_handlers.py +99 -154
  114. claude_mpm/hooks/claude_hooks/hook_handler.py +110 -720
  115. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
  116. claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
  117. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
  118. claude_mpm/hooks/claude_hooks/memory_integration.py +2 -4
  119. claude_mpm/hooks/claude_hooks/response_tracking.py +15 -11
  120. claude_mpm/hooks/claude_hooks/services/__init__.py +13 -0
  121. claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
  122. claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
  123. claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
  124. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
  125. claude_mpm/hooks/claude_hooks/tool_analysis.py +12 -18
  126. claude_mpm/hooks/memory_integration_hook.py +5 -5
  127. claude_mpm/hooks/tool_call_interceptor.py +1 -1
  128. claude_mpm/hooks/validation_hooks.py +4 -4
  129. claude_mpm/init.py +4 -9
  130. claude_mpm/models/__init__.py +2 -2
  131. claude_mpm/models/agent_session.py +11 -14
  132. claude_mpm/scripts/mcp_server.py +20 -11
  133. claude_mpm/scripts/mcp_wrapper.py +5 -5
  134. claude_mpm/scripts/mpm_doctor.py +321 -0
  135. claude_mpm/scripts/socketio_daemon.py +28 -25
  136. claude_mpm/scripts/socketio_daemon_hardened.py +298 -258
  137. claude_mpm/scripts/socketio_server_manager.py +116 -95
  138. claude_mpm/services/__init__.py +49 -49
  139. claude_mpm/services/agent_capabilities_service.py +12 -18
  140. claude_mpm/services/agents/__init__.py +22 -22
  141. claude_mpm/services/agents/agent_builder.py +140 -119
  142. claude_mpm/services/agents/deployment/__init__.py +3 -3
  143. claude_mpm/services/agents/deployment/agent_config_provider.py +9 -9
  144. claude_mpm/services/agents/deployment/agent_configuration_manager.py +19 -20
  145. claude_mpm/services/agents/deployment/agent_definition_factory.py +1 -5
  146. claude_mpm/services/agents/deployment/agent_deployment.py +129 -511
  147. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -8
  148. claude_mpm/services/agents/deployment/agent_environment_manager.py +2 -7
  149. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +6 -10
  150. claude_mpm/services/agents/deployment/agent_format_converter.py +11 -15
  151. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +2 -3
  152. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -5
  153. claude_mpm/services/agents/deployment/agent_metrics_collector.py +13 -19
  154. claude_mpm/services/agents/deployment/agent_restore_handler.py +0 -1
  155. claude_mpm/services/agents/deployment/agent_template_builder.py +26 -35
  156. claude_mpm/services/agents/deployment/agent_validator.py +0 -1
  157. claude_mpm/services/agents/deployment/agent_version_manager.py +7 -9
  158. claude_mpm/services/agents/deployment/agent_versioning.py +3 -3
  159. claude_mpm/services/agents/deployment/agents_directory_resolver.py +6 -7
  160. claude_mpm/services/agents/deployment/async_agent_deployment.py +51 -38
  161. claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
  162. claude_mpm/services/agents/deployment/config/__init__.py +1 -1
  163. claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
  164. claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
  165. claude_mpm/services/agents/deployment/deployment_type_detector.py +1 -1
  166. claude_mpm/services/agents/deployment/deployment_wrapper.py +18 -18
  167. claude_mpm/services/agents/deployment/facade/__init__.py +1 -1
  168. claude_mpm/services/agents/deployment/facade/deployment_executor.py +0 -3
  169. claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -4
  170. claude_mpm/services/agents/deployment/interface_adapter.py +5 -7
  171. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +345 -276
  172. claude_mpm/services/agents/deployment/pipeline/__init__.py +2 -2
  173. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +1 -1
  174. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +6 -4
  175. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +3 -3
  176. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +2 -2
  177. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +14 -13
  178. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +0 -1
  179. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +1 -1
  180. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +8 -9
  181. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +1 -1
  182. claude_mpm/services/agents/deployment/processors/__init__.py +1 -1
  183. claude_mpm/services/agents/deployment/processors/agent_processor.py +20 -16
  184. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +5 -12
  185. claude_mpm/services/agents/deployment/results/__init__.py +1 -1
  186. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +1 -1
  187. claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
  188. claude_mpm/services/agents/deployment/strategies/__init__.py +2 -2
  189. claude_mpm/services/agents/deployment/strategies/base_strategy.py +1 -7
  190. claude_mpm/services/agents/deployment/strategies/project_strategy.py +1 -4
  191. claude_mpm/services/agents/deployment/strategies/system_strategy.py +2 -3
  192. claude_mpm/services/agents/deployment/strategies/user_strategy.py +3 -7
  193. claude_mpm/services/agents/deployment/validation/__init__.py +1 -1
  194. claude_mpm/services/agents/deployment/validation/agent_validator.py +1 -1
  195. claude_mpm/services/agents/deployment/validation/template_validator.py +2 -2
  196. claude_mpm/services/agents/deployment/validation/validation_result.py +2 -6
  197. claude_mpm/services/agents/loading/__init__.py +1 -1
  198. claude_mpm/services/agents/loading/agent_profile_loader.py +6 -12
  199. claude_mpm/services/agents/loading/base_agent_manager.py +5 -5
  200. claude_mpm/services/agents/loading/framework_agent_loader.py +2 -4
  201. claude_mpm/services/agents/management/__init__.py +1 -1
  202. claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -3
  203. claude_mpm/services/agents/management/agent_management_service.py +5 -9
  204. claude_mpm/services/agents/memory/__init__.py +4 -4
  205. claude_mpm/services/agents/memory/agent_memory_manager.py +157 -503
  206. claude_mpm/services/agents/memory/agent_persistence_service.py +0 -2
  207. claude_mpm/services/agents/memory/content_manager.py +44 -38
  208. claude_mpm/services/agents/memory/memory_categorization_service.py +165 -0
  209. claude_mpm/services/agents/memory/memory_file_service.py +103 -0
  210. claude_mpm/services/agents/memory/memory_format_service.py +201 -0
  211. claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
  212. claude_mpm/services/agents/memory/template_generator.py +4 -6
  213. claude_mpm/services/agents/registry/__init__.py +11 -7
  214. claude_mpm/services/agents/registry/deployed_agent_discovery.py +30 -27
  215. claude_mpm/services/agents/registry/modification_tracker.py +3 -6
  216. claude_mpm/services/async_session_logger.py +1 -2
  217. claude_mpm/services/claude_session_logger.py +1 -2
  218. claude_mpm/services/cli/__init__.py +18 -0
  219. claude_mpm/services/cli/agent_cleanup_service.py +407 -0
  220. claude_mpm/services/cli/agent_dependency_service.py +395 -0
  221. claude_mpm/services/cli/agent_listing_service.py +463 -0
  222. claude_mpm/services/cli/agent_output_formatter.py +605 -0
  223. claude_mpm/services/cli/agent_validation_service.py +589 -0
  224. claude_mpm/services/cli/dashboard_launcher.py +424 -0
  225. claude_mpm/services/cli/memory_crud_service.py +617 -0
  226. claude_mpm/services/cli/memory_output_formatter.py +604 -0
  227. claude_mpm/services/cli/session_manager.py +513 -0
  228. claude_mpm/services/cli/socketio_manager.py +498 -0
  229. claude_mpm/services/cli/startup_checker.py +370 -0
  230. claude_mpm/services/command_deployment_service.py +173 -0
  231. claude_mpm/services/command_handler_service.py +20 -22
  232. claude_mpm/services/core/__init__.py +25 -25
  233. claude_mpm/services/core/base.py +0 -5
  234. claude_mpm/services/core/cache_manager.py +311 -0
  235. claude_mpm/services/core/interfaces/__init__.py +32 -32
  236. claude_mpm/services/core/interfaces/agent.py +0 -21
  237. claude_mpm/services/core/interfaces/communication.py +0 -27
  238. claude_mpm/services/core/interfaces/infrastructure.py +0 -56
  239. claude_mpm/services/core/interfaces/service.py +0 -29
  240. claude_mpm/services/core/memory_manager.py +637 -0
  241. claude_mpm/services/core/path_resolver.py +498 -0
  242. claude_mpm/services/core/service_container.py +520 -0
  243. claude_mpm/services/core/service_interfaces.py +436 -0
  244. claude_mpm/services/diagnostics/__init__.py +1 -1
  245. claude_mpm/services/diagnostics/checks/__init__.py +6 -6
  246. claude_mpm/services/diagnostics/checks/agent_check.py +152 -97
  247. claude_mpm/services/diagnostics/checks/base_check.py +12 -16
  248. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +84 -81
  249. claude_mpm/services/diagnostics/checks/common_issues_check.py +99 -91
  250. claude_mpm/services/diagnostics/checks/configuration_check.py +82 -77
  251. claude_mpm/services/diagnostics/checks/filesystem_check.py +67 -68
  252. claude_mpm/services/diagnostics/checks/installation_check.py +254 -94
  253. claude_mpm/services/diagnostics/checks/mcp_check.py +90 -88
  254. claude_mpm/services/diagnostics/checks/monitor_check.py +75 -76
  255. claude_mpm/services/diagnostics/checks/startup_log_check.py +67 -73
  256. claude_mpm/services/diagnostics/diagnostic_runner.py +67 -59
  257. claude_mpm/services/diagnostics/doctor_reporter.py +107 -70
  258. claude_mpm/services/diagnostics/models.py +21 -19
  259. claude_mpm/services/event_aggregator.py +10 -17
  260. claude_mpm/services/event_bus/__init__.py +1 -1
  261. claude_mpm/services/event_bus/config.py +54 -35
  262. claude_mpm/services/event_bus/event_bus.py +76 -71
  263. claude_mpm/services/event_bus/relay.py +74 -64
  264. claude_mpm/services/events/__init__.py +11 -11
  265. claude_mpm/services/events/consumers/__init__.py +3 -3
  266. claude_mpm/services/events/consumers/dead_letter.py +71 -63
  267. claude_mpm/services/events/consumers/logging.py +39 -37
  268. claude_mpm/services/events/consumers/metrics.py +56 -57
  269. claude_mpm/services/events/consumers/socketio.py +82 -81
  270. claude_mpm/services/events/core.py +110 -99
  271. claude_mpm/services/events/interfaces.py +56 -72
  272. claude_mpm/services/events/producers/__init__.py +1 -1
  273. claude_mpm/services/events/producers/hook.py +38 -38
  274. claude_mpm/services/events/producers/system.py +46 -44
  275. claude_mpm/services/exceptions.py +81 -80
  276. claude_mpm/services/framework_claude_md_generator/__init__.py +2 -4
  277. claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -5
  278. claude_mpm/services/framework_claude_md_generator/content_validator.py +1 -1
  279. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +4 -4
  280. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +0 -1
  281. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +0 -2
  282. claude_mpm/services/framework_claude_md_generator/version_manager.py +4 -5
  283. claude_mpm/services/hook_service.py +6 -9
  284. claude_mpm/services/infrastructure/__init__.py +1 -1
  285. claude_mpm/services/infrastructure/context_preservation.py +8 -12
  286. claude_mpm/services/infrastructure/monitoring.py +21 -23
  287. claude_mpm/services/mcp_gateway/__init__.py +37 -37
  288. claude_mpm/services/mcp_gateway/auto_configure.py +95 -103
  289. claude_mpm/services/mcp_gateway/config/__init__.py +1 -1
  290. claude_mpm/services/mcp_gateway/config/config_loader.py +23 -25
  291. claude_mpm/services/mcp_gateway/config/config_schema.py +5 -5
  292. claude_mpm/services/mcp_gateway/config/configuration.py +9 -6
  293. claude_mpm/services/mcp_gateway/core/__init__.py +10 -10
  294. claude_mpm/services/mcp_gateway/core/base.py +0 -3
  295. claude_mpm/services/mcp_gateway/core/interfaces.py +1 -38
  296. claude_mpm/services/mcp_gateway/core/process_pool.py +99 -93
  297. claude_mpm/services/mcp_gateway/core/singleton_manager.py +65 -62
  298. claude_mpm/services/mcp_gateway/core/startup_verification.py +75 -74
  299. claude_mpm/services/mcp_gateway/main.py +2 -1
  300. claude_mpm/services/mcp_gateway/registry/service_registry.py +5 -8
  301. claude_mpm/services/mcp_gateway/registry/tool_registry.py +1 -1
  302. claude_mpm/services/mcp_gateway/server/__init__.py +1 -1
  303. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +12 -19
  304. claude_mpm/services/mcp_gateway/server/stdio_handler.py +4 -3
  305. claude_mpm/services/mcp_gateway/server/stdio_server.py +79 -71
  306. claude_mpm/services/mcp_gateway/tools/__init__.py +2 -2
  307. claude_mpm/services/mcp_gateway/tools/base_adapter.py +5 -6
  308. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +13 -22
  309. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +79 -78
  310. claude_mpm/services/mcp_gateway/tools/hello_world.py +12 -14
  311. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +42 -49
  312. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +51 -55
  313. claude_mpm/services/memory/__init__.py +3 -3
  314. claude_mpm/services/memory/builder.py +3 -6
  315. claude_mpm/services/memory/cache/__init__.py +1 -1
  316. claude_mpm/services/memory/cache/shared_prompt_cache.py +3 -5
  317. claude_mpm/services/memory/cache/simple_cache.py +1 -1
  318. claude_mpm/services/memory/indexed_memory.py +5 -7
  319. claude_mpm/services/memory/optimizer.py +7 -10
  320. claude_mpm/services/memory/router.py +8 -9
  321. claude_mpm/services/memory_hook_service.py +48 -34
  322. claude_mpm/services/monitor_build_service.py +77 -73
  323. claude_mpm/services/port_manager.py +130 -108
  324. claude_mpm/services/project/analyzer.py +12 -10
  325. claude_mpm/services/project/registry.py +11 -11
  326. claude_mpm/services/recovery_manager.py +10 -19
  327. claude_mpm/services/response_tracker.py +0 -1
  328. claude_mpm/services/runner_configuration_service.py +19 -20
  329. claude_mpm/services/session_management_service.py +7 -11
  330. claude_mpm/services/shared/__init__.py +1 -1
  331. claude_mpm/services/shared/async_service_base.py +58 -50
  332. claude_mpm/services/shared/config_service_base.py +73 -67
  333. claude_mpm/services/shared/lifecycle_service_base.py +82 -78
  334. claude_mpm/services/shared/manager_base.py +94 -82
  335. claude_mpm/services/shared/service_factory.py +96 -98
  336. claude_mpm/services/socketio/__init__.py +3 -3
  337. claude_mpm/services/socketio/client_proxy.py +5 -5
  338. claude_mpm/services/socketio/event_normalizer.py +199 -181
  339. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  340. claude_mpm/services/socketio/handlers/base.py +5 -4
  341. claude_mpm/services/socketio/handlers/connection.py +163 -136
  342. claude_mpm/services/socketio/handlers/file.py +13 -14
  343. claude_mpm/services/socketio/handlers/git.py +12 -7
  344. claude_mpm/services/socketio/handlers/hook.py +49 -44
  345. claude_mpm/services/socketio/handlers/memory.py +0 -1
  346. claude_mpm/services/socketio/handlers/project.py +0 -1
  347. claude_mpm/services/socketio/handlers/registry.py +37 -19
  348. claude_mpm/services/socketio/migration_utils.py +98 -84
  349. claude_mpm/services/socketio/server/__init__.py +1 -1
  350. claude_mpm/services/socketio/server/broadcaster.py +81 -87
  351. claude_mpm/services/socketio/server/core.py +65 -54
  352. claude_mpm/services/socketio/server/eventbus_integration.py +95 -56
  353. claude_mpm/services/socketio/server/main.py +64 -38
  354. claude_mpm/services/socketio_client_manager.py +10 -12
  355. claude_mpm/services/subprocess_launcher_service.py +4 -7
  356. claude_mpm/services/system_instructions_service.py +13 -14
  357. claude_mpm/services/ticket_manager.py +2 -2
  358. claude_mpm/services/utility_service.py +5 -13
  359. claude_mpm/services/version_control/__init__.py +16 -16
  360. claude_mpm/services/version_control/branch_strategy.py +5 -8
  361. claude_mpm/services/version_control/conflict_resolution.py +9 -23
  362. claude_mpm/services/version_control/git_operations.py +5 -7
  363. claude_mpm/services/version_control/semantic_versioning.py +16 -17
  364. claude_mpm/services/version_control/version_parser.py +13 -18
  365. claude_mpm/services/version_service.py +10 -11
  366. claude_mpm/storage/__init__.py +1 -1
  367. claude_mpm/storage/state_storage.py +22 -28
  368. claude_mpm/utils/__init__.py +6 -6
  369. claude_mpm/utils/agent_dependency_loader.py +47 -33
  370. claude_mpm/utils/config_manager.py +11 -14
  371. claude_mpm/utils/dependency_cache.py +1 -1
  372. claude_mpm/utils/dependency_manager.py +13 -17
  373. claude_mpm/utils/dependency_strategies.py +8 -10
  374. claude_mpm/utils/environment_context.py +3 -9
  375. claude_mpm/utils/error_handler.py +3 -13
  376. claude_mpm/utils/file_utils.py +1 -1
  377. claude_mpm/utils/path_operations.py +8 -12
  378. claude_mpm/utils/robust_installer.py +110 -33
  379. claude_mpm/utils/subprocess_utils.py +5 -6
  380. claude_mpm/validation/agent_validator.py +3 -6
  381. claude_mpm/validation/frontmatter_validator.py +1 -1
  382. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/METADATA +1 -1
  383. claude_mpm-4.1.3.dist-info/RECORD +528 -0
  384. claude_mpm/cli/commands/run_config_checker.py +0 -160
  385. claude_mpm-4.1.1.dist-info/RECORD +0 -494
  386. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/WHEEL +0 -0
  387. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/entry_points.txt +0 -0
  388. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/licenses/LICENSE +0 -0
  389. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
3
  from pathlib import Path
4
+
4
5
  """
5
6
  Agent Memory Manager Service
6
7
  ===========================
@@ -22,13 +23,17 @@ following the naming convention: {agent_id}_memories.md
22
23
 
23
24
  import logging
24
25
  import os
25
- from datetime import datetime
26
26
  from typing import Any, Dict, List, Optional, Tuple
27
27
 
28
28
  from claude_mpm.core.config import Config
29
29
  from claude_mpm.core.interfaces import MemoryServiceInterface
30
30
  from claude_mpm.core.unified_paths import get_path_manager
31
+
31
32
  from .content_manager import MemoryContentManager
33
+ from .memory_categorization_service import MemoryCategorizationService
34
+ from .memory_file_service import MemoryFileService
35
+ from .memory_format_service import MemoryFormatService
36
+ from .memory_limits_service import MemoryLimitsService
32
37
  from .template_generator import MemoryTemplateGenerator
33
38
 
34
39
 
@@ -74,18 +79,22 @@ class AgentMemoryManager(MemoryServiceInterface):
74
79
  self.project_root = get_path_manager().project_root
75
80
  # Use current working directory by default, not project root
76
81
  self.working_directory = working_directory or Path(os.getcwd())
77
-
82
+
78
83
  # Use only project memory directory
79
84
  self.project_memories_dir = self.working_directory / ".claude-mpm" / "memories"
80
-
85
+
81
86
  # Primary memories_dir points to project
82
87
  self.memories_dir = self.project_memories_dir
83
-
84
- # Ensure project directory exists
85
- self._ensure_memories_directory()
86
88
 
87
- # Initialize memory limits from configuration
88
- self._init_memory_limits()
89
+ # Initialize services
90
+ self.file_service = MemoryFileService(self.memories_dir)
91
+ self.limits_service = MemoryLimitsService(self.config)
92
+ self.memory_limits = self.limits_service.memory_limits
93
+ self.format_service = MemoryFormatService()
94
+ self.categorization_service = MemoryCategorizationService()
95
+
96
+ # Ensure project directory exists
97
+ self.file_service.ensure_memories_directory()
89
98
 
90
99
  # Initialize component services
91
100
  self.template_generator = MemoryTemplateGenerator(
@@ -112,124 +121,6 @@ class AgentMemoryManager(MemoryServiceInterface):
112
121
 
113
122
  return self._logger_instance
114
123
 
115
- def _init_memory_limits(self):
116
- """Initialize memory limits from configuration.
117
-
118
- WHY: Allows configuration-driven memory limits instead of hardcoded values.
119
- Supports agent-specific overrides for different memory requirements.
120
- """
121
- # Check if memory system is enabled
122
- self.memory_enabled = self.config.get("memory.enabled", True)
123
- self.auto_learning = self.config.get(
124
- "memory.auto_learning", True
125
- ) # Changed default to True
126
-
127
- # Load default limits from configuration
128
- config_limits = self.config.get("memory.limits", {})
129
- self.memory_limits = {
130
- "max_file_size_kb": config_limits.get(
131
- "default_size_kb", self.DEFAULT_MEMORY_LIMITS["max_file_size_kb"]
132
- ),
133
- "max_items": config_limits.get(
134
- "max_items", self.DEFAULT_MEMORY_LIMITS["max_items"]
135
- ),
136
- "max_line_length": config_limits.get(
137
- "max_line_length", self.DEFAULT_MEMORY_LIMITS["max_line_length"]
138
- ),
139
- }
140
-
141
- # Load agent-specific overrides
142
- self.agent_overrides = self.config.get("memory.agent_overrides", {})
143
-
144
- def _get_agent_limits(self, agent_id: str) -> Dict[str, Any]:
145
- """Get memory limits for specific agent, including overrides.
146
-
147
- WHY: Different agents may need different memory capacities. Research agents
148
- might need larger memory for comprehensive findings, while simple agents
149
- can work with smaller limits.
150
-
151
- Args:
152
- agent_id: The agent identifier
153
-
154
- Returns:
155
- Dict containing the effective limits for this agent
156
- """
157
- # Start with default limits
158
- limits = self.memory_limits.copy()
159
-
160
- # Apply agent-specific overrides if they exist
161
- if agent_id in self.agent_overrides:
162
- overrides = self.agent_overrides[agent_id]
163
- if "size_kb" in overrides:
164
- limits["max_file_size_kb"] = overrides["size_kb"]
165
-
166
- return limits
167
-
168
- def _get_agent_auto_learning(self, agent_id: str) -> bool:
169
- """Check if auto-learning is enabled for specific agent.
170
-
171
- Args:
172
- agent_id: The agent identifier
173
-
174
- Returns:
175
- bool: True if auto-learning is enabled for this agent
176
- """
177
- # Check agent-specific override first
178
- if agent_id in self.agent_overrides:
179
- return self.agent_overrides[agent_id].get(
180
- "auto_learning", self.auto_learning
181
- )
182
-
183
- # Fall back to global setting
184
- return self.auto_learning
185
-
186
- def _get_memory_file_with_migration(self, directory: Path, agent_id: str) -> Path:
187
- """Get memory file path, migrating from old naming if needed.
188
-
189
- WHY: Supports backward compatibility by automatically migrating from
190
- the old {agent_id}_agent.md and {agent_id}.md formats to the new {agent_id}_memories.md format.
191
-
192
- Args:
193
- directory: Directory containing memory files
194
- agent_id: The agent identifier
195
-
196
- Returns:
197
- Path: Path to the memory file (may not exist)
198
- """
199
- new_file = directory / f"{agent_id}_memories.md"
200
- # Support migration from both old formats
201
- old_file_agent = directory / f"{agent_id}_agent.md"
202
- old_file_simple = directory / f"{agent_id}.md"
203
-
204
- # Migrate from old formats if needed
205
- if not new_file.exists():
206
- # Try migrating from {agent_id}_agent.md first
207
- if old_file_agent.exists():
208
- try:
209
- content = old_file_agent.read_text(encoding="utf-8")
210
- new_file.write_text(content, encoding="utf-8")
211
-
212
- # Delete old file for all agents
213
- old_file_agent.unlink()
214
- self.logger.info(f"Migrated memory file from {old_file_agent.name} to {new_file.name}")
215
- except Exception as e:
216
- self.logger.error(f"Failed to migrate memory file for {agent_id}: {e}")
217
- return old_file_agent
218
- # Try migrating from {agent_id}.md
219
- elif old_file_simple.exists():
220
- try:
221
- content = old_file_simple.read_text(encoding="utf-8")
222
- new_file.write_text(content, encoding="utf-8")
223
-
224
- # Delete old file for all agents
225
- old_file_simple.unlink()
226
- self.logger.info(f"Migrated memory file from {old_file_simple.name} to {new_file.name}")
227
- except Exception as e:
228
- self.logger.error(f"Failed to migrate memory file for {agent_id}: {e}")
229
- return old_file_simple
230
-
231
- return new_file
232
-
233
124
  def load_agent_memory(self, agent_id: str) -> str:
234
125
  """Load agent memory file content from project directory.
235
126
 
@@ -244,18 +135,24 @@ class AgentMemoryManager(MemoryServiceInterface):
244
135
  str: The memory file content, creating default if doesn't exist
245
136
  """
246
137
  # All agents use project directory
247
- project_memory_file = self._get_memory_file_with_migration(self.project_memories_dir, agent_id)
248
-
138
+ project_memory_file = self.file_service.get_memory_file_with_migration(
139
+ self.project_memories_dir, agent_id
140
+ )
141
+
249
142
  # Load project-level memory if exists
250
143
  if project_memory_file.exists():
251
144
  try:
252
145
  project_memory = project_memory_file.read_text(encoding="utf-8")
253
- project_memory = self.content_manager.validate_and_repair(project_memory, agent_id)
146
+ project_memory = self.content_manager.validate_and_repair(
147
+ project_memory, agent_id
148
+ )
254
149
  self.logger.debug(f"Loaded project-level memory for {agent_id}")
255
150
  return project_memory
256
151
  except Exception as e:
257
- self.logger.error(f"Error reading project memory file for {agent_id}: {e}")
258
-
152
+ self.logger.error(
153
+ f"Error reading project memory file for {agent_id}: {e}"
154
+ )
155
+
259
156
  # Memory doesn't exist - create default in project directory
260
157
  self.logger.info(f"Creating default memory for agent: {agent_id}")
261
158
  return self._create_default_memory(agent_id)
@@ -310,7 +207,7 @@ class AgentMemoryManager(MemoryServiceInterface):
310
207
  str: The project-specific memory template content
311
208
  """
312
209
  # Get limits for this agent
313
- limits = self._get_agent_limits(agent_id)
210
+ limits = self.limits_service.get_agent_limits(agent_id)
314
211
 
315
212
  # Delegate to template generator
316
213
  template = self.template_generator.create_default_memory(agent_id, limits)
@@ -327,35 +224,6 @@ class AgentMemoryManager(MemoryServiceInterface):
327
224
 
328
225
  return template
329
226
 
330
- def _save_memory_file(self, agent_id: str, content: str) -> bool:
331
- """Save memory content to file.
332
-
333
- WHY: Memory updates need to be persisted atomically to prevent corruption
334
- and ensure learnings are preserved across agent invocations.
335
-
336
- Args:
337
- agent_id: Agent identifier
338
- content: Content to save
339
-
340
- Returns:
341
- bool: True if save succeeded
342
- """
343
- try:
344
- # All agents save to project directory
345
- target_dir = self.project_memories_dir
346
-
347
- # Ensure directory exists
348
- target_dir.mkdir(parents=True, exist_ok=True)
349
-
350
- memory_file = target_dir / f"{agent_id}_memories.md"
351
- memory_file.write_text(content, encoding="utf-8")
352
-
353
- self.logger.info(f"Saved {agent_id} memory to project directory: {memory_file}")
354
- return True
355
- except Exception as e:
356
- self.logger.error(f"Error saving memory for {agent_id}: {e}")
357
- return False
358
-
359
227
  def optimize_memory(self, agent_id: Optional[str] = None) -> Dict[str, Any]:
360
228
  """Optimize agent memory by consolidating/cleaning memories.
361
229
 
@@ -459,25 +327,25 @@ class AgentMemoryManager(MemoryServiceInterface):
459
327
  try:
460
328
  import json
461
329
  import re
462
-
330
+
463
331
  # Log that we're processing memory for this agent
464
332
  is_pm = agent_id.upper() == "PM"
465
333
  self.logger.debug(f"Extracting memory for {agent_id} (is_pm={is_pm})")
466
-
334
+
467
335
  # Look for JSON block in the response
468
336
  # Pattern matches ```json ... ``` blocks
469
- json_pattern = r'```json\s*(.*?)\s*```'
337
+ json_pattern = r"```json\s*(.*?)\s*```"
470
338
  json_matches = re.findall(json_pattern, response, re.DOTALL)
471
-
339
+
472
340
  if not json_matches:
473
341
  # Also try to find inline JSON objects
474
342
  json_pattern2 = r'\{[^{}]*"(?:remember|Remember|MEMORIES)"[^{}]*\}'
475
343
  json_matches = re.findall(json_pattern2, response, re.DOTALL)
476
-
344
+
477
345
  for json_str in json_matches:
478
346
  try:
479
347
  data = json.loads(json_str)
480
-
348
+
481
349
  # Check for complete memory replacement in "MEMORIES" field
482
350
  if "MEMORIES" in data and data["MEMORIES"] is not None:
483
351
  memories = data["MEMORIES"]
@@ -491,26 +359,33 @@ class AgentMemoryManager(MemoryServiceInterface):
491
359
  if not item_text.startswith("-"):
492
360
  item_text = f"- {item_text}"
493
361
  valid_items.append(item_text)
494
-
362
+
495
363
  if valid_items:
496
- self.logger.info(f"Replacing all memories for {agent_id} with {len(valid_items)} items")
497
- success = self.replace_agent_memory(agent_id, valid_items)
364
+ self.logger.info(
365
+ f"Replacing all memories for {agent_id} with {len(valid_items)} items"
366
+ )
367
+ success = self.replace_agent_memory(
368
+ agent_id, valid_items
369
+ )
498
370
  if success:
499
- self.logger.info(f"Successfully replaced memories for {agent_id}")
371
+ self.logger.info(
372
+ f"Successfully replaced memories for {agent_id}"
373
+ )
500
374
  return True
501
- else:
502
- self.logger.error(f"Failed to replace memories for {agent_id}")
375
+ self.logger.error(
376
+ f"Failed to replace memories for {agent_id}"
377
+ )
503
378
  continue # Skip checking remember field if MEMORIES was processed
504
-
379
+
505
380
  # Check for incremental memory updates in "remember" field
506
381
  memory_items = None
507
-
382
+
508
383
  # Check both "remember" and "Remember" fields
509
384
  if "remember" in data:
510
385
  memory_items = data["remember"]
511
386
  elif "Remember" in data:
512
387
  memory_items = data["Remember"]
513
-
388
+
514
389
  # Process memory items if found and not null
515
390
  if memory_items is not None and memory_items != "null":
516
391
  # Skip if explicitly null or empty list
@@ -520,269 +395,120 @@ class AgentMemoryManager(MemoryServiceInterface):
520
395
  for item in memory_items:
521
396
  if item and isinstance(item, str) and item.strip():
522
397
  valid_items.append(item.strip())
523
-
398
+
524
399
  # Only proceed if we have valid items
525
400
  if valid_items:
526
- self.logger.info(f"Found {len(valid_items)} memory items for {agent_id}: {valid_items[:2]}...")
527
- success = self._add_learnings_to_memory(agent_id, valid_items)
401
+ self.logger.info(
402
+ f"Found {len(valid_items)} memory items for {agent_id}: {valid_items[:2]}..."
403
+ )
404
+ success = self._add_learnings_to_memory(
405
+ agent_id, valid_items
406
+ )
528
407
  if success:
529
- self.logger.info(f"Successfully saved {len(valid_items)} memories for {agent_id} to project directory")
408
+ self.logger.info(
409
+ f"Successfully saved {len(valid_items)} memories for {agent_id} to project directory"
410
+ )
530
411
  return True
531
- else:
532
- self.logger.error(f"Failed to save memories for {agent_id}")
533
-
412
+ self.logger.error(
413
+ f"Failed to save memories for {agent_id}"
414
+ )
415
+
534
416
  except json.JSONDecodeError as je:
535
417
  # Not valid JSON, continue to next match
536
418
  self.logger.debug(f"JSON decode error for {agent_id}: {je}")
537
419
  continue
538
-
420
+
539
421
  self.logger.debug(f"No memory items found in response for {agent_id}")
540
422
  return False
541
-
423
+
542
424
  except Exception as e:
543
- self.logger.error(f"Error extracting memory from response for {agent_id}: {e}")
425
+ self.logger.error(
426
+ f"Error extracting memory from response for {agent_id}: {e}"
427
+ )
544
428
  return False
545
-
429
+
546
430
  def _add_learnings_to_memory(self, agent_id: str, learnings: List[str]) -> bool:
547
431
  """Add new learnings to agent memory as a simple list.
548
-
432
+
549
433
  WHY: Simplified memory system - all memories are stored as a simple list
550
434
  without categorization, making it easier to manage and understand.
551
435
  Updates timestamp on every update.
552
-
436
+
553
437
  Args:
554
438
  agent_id: The agent identifier
555
439
  learnings: List of new learning strings to add
556
-
440
+
557
441
  Returns:
558
442
  bool: True if memory was successfully updated
559
443
  """
560
444
  try:
561
445
  # Load existing memory
562
446
  current_memory = self.load_agent_memory(agent_id)
563
-
447
+
564
448
  # Parse existing memory into a simple list
565
- existing_items = self._parse_memory_list(current_memory)
566
-
449
+ existing_items = self.format_service.parse_memory_list(current_memory)
450
+
567
451
  # Clean template placeholders if this is a fresh memory
568
- existing_items = self._clean_template_placeholders_list(existing_items)
569
-
452
+ existing_items = self.format_service.clean_template_placeholders_list(
453
+ existing_items
454
+ )
455
+
570
456
  # Add new learnings, avoiding duplicates
571
457
  updated = False
572
458
  for learning in learnings:
573
459
  if not learning or not isinstance(learning, str):
574
460
  continue
575
-
461
+
576
462
  learning = learning.strip()
577
463
  if not learning:
578
464
  continue
579
-
465
+
580
466
  # Check for duplicates (case-insensitive)
581
467
  normalized_learning = learning.lower()
582
468
  # Strip bullet points from existing items for comparison
583
- existing_normalized = [item.lstrip('- ').strip().lower() for item in existing_items]
584
-
469
+ existing_normalized = [
470
+ item.lstrip("- ").strip().lower() for item in existing_items
471
+ ]
472
+
585
473
  if normalized_learning not in existing_normalized:
586
474
  # Add bullet point if not present
587
475
  if not learning.startswith("-"):
588
476
  learning = f"- {learning}"
589
477
  existing_items.append(learning)
590
- self.logger.info(f"Added new memory for {agent_id}: {learning[:50]}...")
478
+ self.logger.info(
479
+ f"Added new memory for {agent_id}: {learning[:50]}..."
480
+ )
591
481
  updated = True
592
482
  else:
593
- self.logger.debug(f"Skipping duplicate memory for {agent_id}: {learning}")
594
-
483
+ self.logger.debug(
484
+ f"Skipping duplicate memory for {agent_id}: {learning}"
485
+ )
486
+
595
487
  # Only save if we actually added new items
596
488
  if not updated:
597
489
  self.logger.debug(f"No new memories to add for {agent_id}")
598
490
  return True # Not an error, just nothing new to add
599
-
491
+
600
492
  # Rebuild memory content as simple list with updated timestamp
601
- new_content = self._build_simple_memory_content(agent_id, existing_items)
602
-
493
+ new_content = self.format_service.build_simple_memory_content(
494
+ agent_id, existing_items
495
+ )
496
+
603
497
  # Validate and save
604
- agent_limits = self._get_agent_limits(agent_id)
498
+ agent_limits = self.limits_service.get_agent_limits(agent_id)
605
499
  if self.content_manager.exceeds_limits(new_content, agent_limits):
606
500
  self.logger.debug(f"Memory for {agent_id} exceeds limits, truncating")
607
- new_content = self.content_manager.truncate_simple_list(new_content, agent_limits)
608
-
501
+ new_content = self.content_manager.truncate_simple_list(
502
+ new_content, agent_limits
503
+ )
504
+
609
505
  # All memories go to project directory
610
- return self._save_memory_file(agent_id, new_content)
611
-
506
+ return self._save_memory_file_wrapper(agent_id, new_content)
507
+
612
508
  except Exception as e:
613
509
  self.logger.error(f"Error adding learnings to memory for {agent_id}: {e}")
614
510
  return False
615
-
616
- def _parse_memory_list(self, memory_content: str) -> List[str]:
617
- """Parse memory content into a simple list.
618
-
619
- Args:
620
- memory_content: Raw memory file content
621
-
622
- Returns:
623
- List of memory items
624
- """
625
- items = []
626
-
627
- for line in memory_content.split('\n'):
628
- line = line.strip()
629
- # Skip metadata lines and headers
630
- if line.startswith('<!-- ') or line.startswith('#') or not line:
631
- continue
632
- # Collect items (with or without bullet points)
633
- if line.startswith('- '):
634
- items.append(line)
635
- elif line and not line.startswith('##'): # Legacy format without bullets
636
- items.append(f"- {line}")
637
-
638
- return items
639
-
640
- def _clean_template_placeholders_list(self, items: List[str]) -> List[str]:
641
- """Remove template placeholder text from item list.
642
-
643
- Args:
644
- items: List of memory items
645
-
646
- Returns:
647
- List with placeholder text removed
648
- """
649
- # Template placeholder patterns to remove
650
- placeholders = [
651
- "Analyze project structure to understand architecture patterns",
652
- "Observe codebase patterns and conventions during tasks",
653
- "Extract implementation guidelines from project documentation",
654
- "Learn from errors encountered during project work",
655
- "Project analysis pending - gather context during tasks",
656
- "claude-mpm: Software project requiring analysis"
657
- ]
658
-
659
- cleaned = []
660
- for item in items:
661
- # Remove bullet point for comparison
662
- item_text = item.lstrip("- ").strip()
663
- # Keep item if it's not a placeholder
664
- if item_text and item_text not in placeholders:
665
- cleaned.append(item)
666
-
667
- return cleaned
668
-
669
- def _clean_template_placeholders(self, sections: Dict[str, List[str]]) -> Dict[str, List[str]]:
670
- """Remove template placeholder text from sections.
671
-
672
- Args:
673
- sections: Dict mapping section names to lists of items
674
-
675
- Returns:
676
- Dict with placeholder text removed
677
- """
678
- # Template placeholder patterns to remove
679
- placeholders = [
680
- "Analyze project structure to understand architecture patterns",
681
- "Observe codebase patterns and conventions during tasks",
682
- "Extract implementation guidelines from project documentation",
683
- "Learn from errors encountered during project work",
684
- "Project analysis pending - gather context during tasks",
685
- "claude-mpm: Software project requiring analysis"
686
- ]
687
-
688
- cleaned = {}
689
- for section_name, items in sections.items():
690
- cleaned_items = []
691
- for item in items:
692
- # Remove bullet point for comparison
693
- item_text = item.lstrip("- ").strip()
694
- # Keep item if it's not a placeholder
695
- if item_text and item_text not in placeholders:
696
- cleaned_items.append(item)
697
-
698
- # Only include section if it has real content
699
- if cleaned_items:
700
- cleaned[section_name] = cleaned_items
701
-
702
- return cleaned
703
-
704
- def _categorize_learning(self, learning: str) -> str:
705
- """Categorize a learning item into appropriate section.
706
-
707
- Args:
708
- learning: The learning string to categorize
709
-
710
- Returns:
711
- str: The section name for this learning
712
- """
713
- learning_lower = learning.lower()
714
-
715
- # Check for keywords to categorize with improved patterns
716
- # Order matters - more specific patterns should come first
717
-
718
- # Architecture keywords
719
- if any(word in learning_lower for word in ["architecture", "structure", "design", "module", "component", "microservices", "service-oriented"]):
720
- return "Project Architecture"
721
-
722
- # Integration keywords (check before patterns to avoid "use" conflict)
723
- elif any(word in learning_lower for word in ["integration", "interface", "api", "connection", "database", "pooling", "via"]):
724
- return "Integration Points"
725
-
726
- # Mistake keywords (check before patterns to avoid conflicts)
727
- elif any(word in learning_lower for word in ["mistake", "error", "avoid", "don't", "never", "not"]):
728
- return "Common Mistakes to Avoid"
729
-
730
- # Context keywords (check before patterns to avoid "working", "version" conflicts)
731
- elif any(word in learning_lower for word in ["context", "current", "currently", "working", "version", "release", "candidate"]):
732
- return "Current Technical Context"
733
-
734
- # Guideline keywords (check before patterns to avoid "must", "should" conflicts)
735
- elif any(word in learning_lower for word in ["guideline", "rule", "standard", "practice", "docstring", "documentation", "must", "should", "include", "comprehensive"]):
736
- return "Implementation Guidelines"
737
-
738
- # Pattern keywords (including dependency injection, conventions)
739
- elif any(word in learning_lower for word in ["pattern", "convention", "style", "format", "dependency injection", "instantiation", "use", "implement"]):
740
- return "Coding Patterns Learned"
741
-
742
- # Strategy keywords
743
- elif any(word in learning_lower for word in ["strategy", "approach", "method", "technique", "effective"]):
744
- return "Effective Strategies"
745
-
746
- # Performance keywords
747
- elif any(word in learning_lower for word in ["performance", "optimization", "speed", "efficiency"]):
748
- return "Performance Considerations"
749
-
750
- # Domain keywords
751
- elif any(word in learning_lower for word in ["domain", "business", "specific"]):
752
- return "Domain-Specific Knowledge"
753
-
754
- else:
755
- return "Recent Learnings"
756
-
757
- def _build_simple_memory_content(self, agent_id: str, items: List[str]) -> str:
758
- """Build memory content as a simple list with updated timestamp.
759
-
760
- Args:
761
- agent_id: The agent identifier
762
- items: List of memory items
763
-
764
- Returns:
765
- str: The formatted memory content
766
- """
767
- lines = []
768
-
769
- # Add header
770
- lines.append(f"# Agent Memory: {agent_id}")
771
- # Always update timestamp when building new content
772
- lines.append(f"<!-- Last Updated: {datetime.now().isoformat()}Z -->")
773
- lines.append("")
774
-
775
- # Add all items as a simple list
776
- for item in items:
777
- if item.strip():
778
- # Ensure item has bullet point
779
- if not item.strip().startswith("-"):
780
- lines.append(f"- {item.strip()}")
781
- else:
782
- lines.append(item.strip())
783
-
784
- return '\n'.join(lines)
785
-
511
+
786
512
  def replace_agent_memory(self, agent_id: str, memory_items: List[str]) -> bool:
787
513
  """Replace agent's memory with new content as a simple list.
788
514
 
@@ -799,17 +525,21 @@ class AgentMemoryManager(MemoryServiceInterface):
799
525
  """
800
526
  try:
801
527
  # Build new memory content as simple list with updated timestamp
802
- new_content = self._build_simple_memory_content(agent_id, memory_items)
803
-
528
+ new_content = self.format_service.build_simple_memory_content(
529
+ agent_id, memory_items
530
+ )
531
+
804
532
  # Validate and save
805
- agent_limits = self._get_agent_limits(agent_id)
533
+ agent_limits = self.limits_service.get_agent_limits(agent_id)
806
534
  if self.content_manager.exceeds_limits(new_content, agent_limits):
807
535
  self.logger.debug(f"Memory for {agent_id} exceeds limits, truncating")
808
- new_content = self.content_manager.truncate_simple_list(new_content, agent_limits)
809
-
536
+ new_content = self.content_manager.truncate_simple_list(
537
+ new_content, agent_limits
538
+ )
539
+
810
540
  # Save the new memory
811
- return self._save_memory_file(agent_id, new_content)
812
-
541
+ return self._save_memory_file_wrapper(agent_id, new_content)
542
+
813
543
  except Exception as e:
814
544
  self.logger.error(f"Error replacing memory for {agent_id}: {e}")
815
545
  return False
@@ -832,13 +562,13 @@ class AgentMemoryManager(MemoryServiceInterface):
832
562
  "total_agents": 0,
833
563
  "total_size_kb": 0,
834
564
  "agents": {},
835
- "system_health": "healthy"
565
+ "system_health": "healthy",
836
566
  }
837
-
567
+
838
568
  if self.memories_dir.exists():
839
569
  memory_files = list(self.memories_dir.glob("*_memories.md"))
840
570
  status["total_agents"] = len(memory_files)
841
-
571
+
842
572
  for file_path in memory_files:
843
573
  if file_path.name != "README.md":
844
574
  size_kb = file_path.stat().st_size / 1024
@@ -846,9 +576,9 @@ class AgentMemoryManager(MemoryServiceInterface):
846
576
  agent_id = file_path.stem.replace("_memories", "")
847
577
  status["agents"][agent_id] = {
848
578
  "file": file_path.name,
849
- "size_kb": round(size_kb, 2)
579
+ "size_kb": round(size_kb, 2),
850
580
  }
851
-
581
+
852
582
  return status
853
583
 
854
584
  def cross_reference_memories(self, query: Optional[str] = None) -> Dict[str, Any]:
@@ -868,7 +598,7 @@ class AgentMemoryManager(MemoryServiceInterface):
868
598
  return {
869
599
  "status": "deprecated",
870
600
  "message": "Cross-reference analysis has been deprecated in favor of simplified memory management",
871
- "suggestion": "Use get_memory_status() for memory overview"
601
+ "suggestion": "Use get_memory_status() for memory overview",
872
602
  }
873
603
 
874
604
  def get_all_memories_raw(self) -> Dict[str, Any]:
@@ -883,110 +613,25 @@ class AgentMemoryManager(MemoryServiceInterface):
883
613
  """
884
614
  # Deprecated - return informative message
885
615
  return {
886
- "status": "deprecated",
616
+ "status": "deprecated",
887
617
  "message": "Raw memory access has been deprecated in favor of simplified memory management",
888
- "suggestion": "Use load_agent_memory() for specific agent memories"
618
+ "suggestion": "Use load_agent_memory() for specific agent memories",
889
619
  }
890
620
 
891
- def _ensure_memories_directory(self):
892
- """Ensure memories directory exists with README.
893
-
894
- WHY: The memories directory needs clear documentation so developers
895
- understand the purpose of these files and how to interact with them.
896
- """
897
- try:
898
- self.memories_dir.mkdir(parents=True, exist_ok=True)
899
- self.logger.debug(f"Ensured memories directory exists: {self.memories_dir}")
900
-
901
- readme_path = self.memories_dir / "README.md"
902
- if not readme_path.exists():
903
- readme_content = """# Agent Memory System
904
-
905
- ## Purpose
906
- Each agent maintains project-specific knowledge in these files. Agents read their memory file before tasks and update it when they learn something new.
907
-
908
- ## Manual Editing
909
- Feel free to edit these files to:
910
- - Add project-specific guidelines
911
- - Remove outdated information
912
- - Reorganize for better clarity
913
- - Add domain-specific knowledge
914
-
915
- ## Memory Limits
916
- - Max file size: 80KB (~20k tokens)
917
- - Max sections: 10
918
- - Max items per section: 15
919
- - Files auto-truncate when limits exceeded
920
-
921
- ## File Format
922
- Standard markdown with structured sections. Agents expect:
923
- - Project Architecture
924
- - Implementation Guidelines
925
- - Common Mistakes to Avoid
926
- - Current Technical Context
927
-
928
- ## How It Works
929
- 1. Agents read their memory file before starting tasks
930
- 2. Agents add learnings during or after task completion
931
- 3. Files automatically enforce size limits
932
- 4. Developers can manually edit for accuracy
933
-
934
- ## Memory File Lifecycle
935
- - Created automatically when agent first runs
936
- - Updated through hook system after delegations
937
- - Manually editable by developers
938
- - Version controlled with project
939
- """
940
- readme_path.write_text(readme_content, encoding="utf-8")
941
- self.logger.info("Created README.md in memories directory")
942
-
943
- except Exception as e:
944
- self.logger.error(f"Error ensuring memories directory: {e}")
945
- # Continue anyway - memory system should not block operations
946
-
621
+ def _save_memory_file_wrapper(self, agent_id: str, content: str) -> bool:
622
+ """Wrapper for save_memory_file that handles agent_id.
947
623
 
948
-
949
- def _parse_memory_sections(self, memory_content: str) -> Dict[str, List[str]]:
950
- """Parse memory content into sections and items.
951
-
952
624
  Args:
953
- memory_content: Raw memory file content
954
-
625
+ agent_id: Agent identifier
626
+ content: Content to save
627
+
955
628
  Returns:
956
- Dict mapping section names to lists of items
629
+ True if saved successfully
957
630
  """
958
- sections = {}
959
- current_section = None
960
- current_items = []
961
-
962
- for line in memory_content.split('\n'):
963
- # Skip metadata lines
964
- if line.startswith('<!-- ') and line.endswith(' -->'):
965
- continue
966
- # Check for section headers (## Level 2 headers)
967
- elif line.startswith('## '):
968
- # Save previous section if exists
969
- if current_section and current_items:
970
- sections[current_section] = current_items
971
-
972
- # Start new section
973
- current_section = line[3:].strip() # Remove "## " prefix
974
- current_items = []
975
- # Collect non-empty lines as items (but not HTML comments)
976
- elif line.strip() and current_section and not line.strip().startswith('<!--'):
977
- # Keep the full line with its formatting
978
- current_items.append(line.strip())
979
-
980
- # Save last section
981
- if current_section and current_items:
982
- sections[current_section] = current_items
983
-
984
- return sections
985
-
986
- # ================================================================================
987
- # Interface Adapter Methods
988
- # ================================================================================
989
- # These methods adapt the existing implementation to comply with MemoryServiceInterface
631
+ file_path = self.file_service.get_memory_file_with_migration(
632
+ self.memories_dir, agent_id
633
+ )
634
+ return self.file_service.save_memory_file(file_path, content)
990
635
 
991
636
  def load_memory(self, agent_id: str) -> Optional[str]:
992
637
  """Load memory for a specific agent.
@@ -1066,12 +711,8 @@ Standard markdown with structured sections. Agents expect:
1066
711
  Dictionary with memory metrics
1067
712
  """
1068
713
  # Minimal implementation for interface compliance
1069
- metrics = {
1070
- "total_memory_kb": 0,
1071
- "agent_count": 0,
1072
- "agents": {}
1073
- }
1074
-
714
+ metrics = {"total_memory_kb": 0, "agent_count": 0, "agents": {}}
715
+
1075
716
  if self.memories_dir.exists():
1076
717
  if agent_id:
1077
718
  # Metrics for specific agent
@@ -1080,8 +721,19 @@ Standard markdown with structured sections. Agents expect:
1080
721
  size_kb = memory_file.stat().st_size / 1024
1081
722
  metrics["agents"][agent_id] = {
1082
723
  "size_kb": round(size_kb, 2),
1083
- "limit_kb": self._get_agent_limits(agent_id)["max_file_size_kb"],
1084
- "usage_percent": round((size_kb / self._get_agent_limits(agent_id)["max_file_size_kb"]) * 100, 1)
724
+ "limit_kb": self.limits_service.get_agent_limits(agent_id)[
725
+ "max_file_size_kb"
726
+ ],
727
+ "usage_percent": round(
728
+ (
729
+ size_kb
730
+ / self.limits_service.get_agent_limits(agent_id)[
731
+ "max_file_size_kb"
732
+ ]
733
+ )
734
+ * 100,
735
+ 1,
736
+ ),
1085
737
  }
1086
738
  metrics["total_memory_kb"] = round(size_kb, 2)
1087
739
  metrics["agent_count"] = 1
@@ -1092,17 +744,19 @@ Standard markdown with structured sections. Agents expect:
1092
744
  if file_path.name != "README.md":
1093
745
  agent_name = file_path.stem.replace("_memories", "")
1094
746
  size_kb = file_path.stat().st_size / 1024
1095
- limit_kb = self._get_agent_limits(agent_name)["max_file_size_kb"]
747
+ limit_kb = self.limits_service.get_agent_limits(agent_name)[
748
+ "max_file_size_kb"
749
+ ]
1096
750
  metrics["agents"][agent_name] = {
1097
751
  "size_kb": round(size_kb, 2),
1098
752
  "limit_kb": limit_kb,
1099
- "usage_percent": round((size_kb / limit_kb) * 100, 1)
753
+ "usage_percent": round((size_kb / limit_kb) * 100, 1),
1100
754
  }
1101
755
  metrics["total_memory_kb"] += size_kb
1102
-
756
+
1103
757
  metrics["total_memory_kb"] = round(metrics["total_memory_kb"], 2)
1104
758
  metrics["agent_count"] = len(metrics["agents"])
1105
-
759
+
1106
760
  return metrics
1107
761
 
1108
762