claude-mpm 3.9.11__py3-none-any.whl → 4.0.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 (419) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +2 -2
  3. claude_mpm/__main__.py +3 -2
  4. claude_mpm/agents/__init__.py +85 -79
  5. claude_mpm/agents/agent_loader.py +464 -1003
  6. claude_mpm/agents/agent_loader_integration.py +45 -45
  7. claude_mpm/agents/agents_metadata.py +29 -30
  8. claude_mpm/agents/async_agent_loader.py +156 -138
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/base_agent_loader.py +179 -151
  11. claude_mpm/agents/frontmatter_validator.py +229 -130
  12. claude_mpm/agents/schema/agent_schema.json +1 -1
  13. claude_mpm/agents/system_agent_config.py +213 -147
  14. claude_mpm/agents/templates/__init__.py +13 -13
  15. claude_mpm/agents/templates/code_analyzer.json +2 -2
  16. claude_mpm/agents/templates/data_engineer.json +1 -1
  17. claude_mpm/agents/templates/documentation.json +23 -11
  18. claude_mpm/agents/templates/engineer.json +22 -6
  19. claude_mpm/agents/templates/memory_manager.json +1 -1
  20. claude_mpm/agents/templates/ops.json +2 -2
  21. claude_mpm/agents/templates/project_organizer.json +1 -1
  22. claude_mpm/agents/templates/qa.json +1 -1
  23. claude_mpm/agents/templates/refactoring_engineer.json +222 -0
  24. claude_mpm/agents/templates/research.json +20 -14
  25. claude_mpm/agents/templates/security.json +1 -1
  26. claude_mpm/agents/templates/ticketing.json +1 -1
  27. claude_mpm/agents/templates/version_control.json +1 -1
  28. claude_mpm/agents/templates/web_qa.json +3 -1
  29. claude_mpm/agents/templates/web_ui.json +2 -2
  30. claude_mpm/cli/__init__.py +79 -51
  31. claude_mpm/cli/__main__.py +3 -2
  32. claude_mpm/cli/commands/__init__.py +20 -20
  33. claude_mpm/cli/commands/agents.py +279 -247
  34. claude_mpm/cli/commands/aggregate.py +138 -157
  35. claude_mpm/cli/commands/cleanup.py +147 -147
  36. claude_mpm/cli/commands/config.py +93 -76
  37. claude_mpm/cli/commands/info.py +17 -16
  38. claude_mpm/cli/commands/mcp.py +140 -905
  39. claude_mpm/cli/commands/mcp_command_router.py +139 -0
  40. claude_mpm/cli/commands/mcp_config_commands.py +20 -0
  41. claude_mpm/cli/commands/mcp_install_commands.py +20 -0
  42. claude_mpm/cli/commands/mcp_server_commands.py +175 -0
  43. claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
  44. claude_mpm/cli/commands/memory.py +239 -203
  45. claude_mpm/cli/commands/monitor.py +203 -81
  46. claude_mpm/cli/commands/run.py +380 -429
  47. claude_mpm/cli/commands/run_config_checker.py +160 -0
  48. claude_mpm/cli/commands/socketio_monitor.py +235 -0
  49. claude_mpm/cli/commands/tickets.py +305 -197
  50. claude_mpm/cli/parser.py +24 -1156
  51. claude_mpm/cli/parsers/__init__.py +29 -0
  52. claude_mpm/cli/parsers/agents_parser.py +136 -0
  53. claude_mpm/cli/parsers/base_parser.py +331 -0
  54. claude_mpm/cli/parsers/config_parser.py +85 -0
  55. claude_mpm/cli/parsers/mcp_parser.py +152 -0
  56. claude_mpm/cli/parsers/memory_parser.py +138 -0
  57. claude_mpm/cli/parsers/monitor_parser.py +104 -0
  58. claude_mpm/cli/parsers/run_parser.py +147 -0
  59. claude_mpm/cli/parsers/tickets_parser.py +203 -0
  60. claude_mpm/cli/ticket_cli.py +7 -3
  61. claude_mpm/cli/utils.py +55 -37
  62. claude_mpm/cli_module/__init__.py +6 -6
  63. claude_mpm/cli_module/args.py +188 -140
  64. claude_mpm/cli_module/commands.py +79 -70
  65. claude_mpm/cli_module/migration_example.py +38 -60
  66. claude_mpm/config/__init__.py +32 -25
  67. claude_mpm/config/agent_config.py +151 -119
  68. claude_mpm/config/experimental_features.py +71 -73
  69. claude_mpm/config/paths.py +94 -208
  70. claude_mpm/config/socketio_config.py +84 -73
  71. claude_mpm/constants.py +35 -18
  72. claude_mpm/core/__init__.py +9 -6
  73. claude_mpm/core/agent_name_normalizer.py +68 -71
  74. claude_mpm/core/agent_registry.py +372 -521
  75. claude_mpm/core/agent_session_manager.py +74 -63
  76. claude_mpm/core/base_service.py +116 -87
  77. claude_mpm/core/cache.py +119 -153
  78. claude_mpm/core/claude_runner.py +425 -1120
  79. claude_mpm/core/config.py +263 -168
  80. claude_mpm/core/config_aliases.py +69 -61
  81. claude_mpm/core/config_constants.py +292 -0
  82. claude_mpm/core/constants.py +57 -99
  83. claude_mpm/core/container.py +211 -178
  84. claude_mpm/core/exceptions.py +233 -89
  85. claude_mpm/core/factories.py +92 -54
  86. claude_mpm/core/framework_loader.py +378 -220
  87. claude_mpm/core/hook_manager.py +198 -83
  88. claude_mpm/core/hook_performance_config.py +136 -0
  89. claude_mpm/core/injectable_service.py +61 -55
  90. claude_mpm/core/interactive_session.py +165 -155
  91. claude_mpm/core/interfaces.py +221 -195
  92. claude_mpm/core/lazy.py +96 -96
  93. claude_mpm/core/logger.py +133 -107
  94. claude_mpm/core/logging_config.py +185 -157
  95. claude_mpm/core/minimal_framework_loader.py +20 -15
  96. claude_mpm/core/mixins.py +30 -29
  97. claude_mpm/core/oneshot_session.py +215 -181
  98. claude_mpm/core/optimized_agent_loader.py +134 -138
  99. claude_mpm/core/optimized_startup.py +159 -157
  100. claude_mpm/core/pm_hook_interceptor.py +85 -72
  101. claude_mpm/core/service_registry.py +103 -101
  102. claude_mpm/core/session_manager.py +97 -87
  103. claude_mpm/core/socketio_pool.py +212 -158
  104. claude_mpm/core/tool_access_control.py +58 -51
  105. claude_mpm/core/types.py +46 -24
  106. claude_mpm/core/typing_utils.py +166 -82
  107. claude_mpm/core/unified_agent_registry.py +721 -0
  108. claude_mpm/core/unified_config.py +550 -0
  109. claude_mpm/core/unified_paths.py +549 -0
  110. claude_mpm/dashboard/index.html +1 -1
  111. claude_mpm/dashboard/open_dashboard.py +51 -17
  112. claude_mpm/dashboard/static/css/dashboard.css +27 -8
  113. claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
  114. claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
  115. claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
  116. claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
  117. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
  118. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
  119. claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
  120. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
  121. claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
  122. claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
  123. claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
  124. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
  125. claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
  126. claude_mpm/dashboard/static/dist/dashboard.js +2 -0
  127. claude_mpm/dashboard/static/dist/socket-client.js +2 -0
  128. claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
  129. claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
  130. claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
  131. claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
  132. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
  133. claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
  134. claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
  135. claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
  136. claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
  137. claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
  138. claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
  139. claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
  140. claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
  141. claude_mpm/dashboard/static/js/dashboard.js +178 -453
  142. claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
  143. claude_mpm/dashboard/static/js/socket-client.js +120 -54
  144. claude_mpm/dashboard/templates/index.html +40 -50
  145. claude_mpm/experimental/cli_enhancements.py +60 -58
  146. claude_mpm/generators/__init__.py +1 -1
  147. claude_mpm/generators/agent_profile_generator.py +75 -65
  148. claude_mpm/hooks/__init__.py +1 -1
  149. claude_mpm/hooks/base_hook.py +33 -28
  150. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  151. claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
  152. claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
  153. claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
  154. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
  155. claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
  156. claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
  157. claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
  158. claude_mpm/hooks/memory_integration_hook.py +140 -100
  159. claude_mpm/hooks/tool_call_interceptor.py +89 -76
  160. claude_mpm/hooks/validation_hooks.py +57 -49
  161. claude_mpm/init.py +145 -121
  162. claude_mpm/models/__init__.py +9 -9
  163. claude_mpm/models/agent_definition.py +33 -23
  164. claude_mpm/models/agent_session.py +228 -200
  165. claude_mpm/scripts/__init__.py +1 -1
  166. claude_mpm/scripts/socketio_daemon.py +192 -75
  167. claude_mpm/scripts/socketio_server_manager.py +328 -0
  168. claude_mpm/scripts/start_activity_logging.py +25 -22
  169. claude_mpm/services/__init__.py +68 -43
  170. claude_mpm/services/agent_capabilities_service.py +271 -0
  171. claude_mpm/services/agents/__init__.py +23 -32
  172. claude_mpm/services/agents/deployment/__init__.py +3 -3
  173. claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
  174. claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
  175. claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
  176. claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
  177. claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
  178. claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
  179. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
  180. claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
  181. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
  182. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
  183. claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
  184. claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
  185. claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
  186. claude_mpm/services/agents/deployment/agent_validator.py +352 -0
  187. claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
  188. claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
  189. claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
  190. claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
  191. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  192. claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
  193. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  194. claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
  195. claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
  196. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  197. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  198. claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
  199. claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
  200. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  201. claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
  202. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  203. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  204. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  205. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  206. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
  207. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  208. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  209. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
  210. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
  211. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
  212. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
  213. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
  214. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  215. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
  216. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  217. claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
  218. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
  219. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  220. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  221. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  222. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  223. claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
  224. claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
  225. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  226. claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
  227. claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
  228. claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
  229. claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
  230. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  231. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  232. claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
  233. claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
  234. claude_mpm/services/agents/loading/__init__.py +2 -2
  235. claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
  236. claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
  237. claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
  238. claude_mpm/services/agents/management/__init__.py +2 -2
  239. claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
  240. claude_mpm/services/agents/management/agent_management_service.py +209 -156
  241. claude_mpm/services/agents/memory/__init__.py +9 -6
  242. claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
  243. claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
  244. claude_mpm/services/agents/memory/analyzer.py +430 -0
  245. claude_mpm/services/agents/memory/content_manager.py +376 -0
  246. claude_mpm/services/agents/memory/template_generator.py +468 -0
  247. claude_mpm/services/agents/registry/__init__.py +7 -10
  248. claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
  249. claude_mpm/services/agents/registry/modification_tracker.py +351 -285
  250. claude_mpm/services/async_session_logger.py +187 -153
  251. claude_mpm/services/claude_session_logger.py +87 -72
  252. claude_mpm/services/command_handler_service.py +217 -0
  253. claude_mpm/services/communication/__init__.py +3 -2
  254. claude_mpm/services/core/__init__.py +50 -97
  255. claude_mpm/services/core/base.py +60 -53
  256. claude_mpm/services/core/interfaces/__init__.py +188 -0
  257. claude_mpm/services/core/interfaces/agent.py +351 -0
  258. claude_mpm/services/core/interfaces/communication.py +343 -0
  259. claude_mpm/services/core/interfaces/infrastructure.py +413 -0
  260. claude_mpm/services/core/interfaces/service.py +434 -0
  261. claude_mpm/services/core/interfaces.py +19 -944
  262. claude_mpm/services/event_aggregator.py +208 -170
  263. claude_mpm/services/exceptions.py +387 -308
  264. claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
  265. claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
  266. claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
  267. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
  268. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
  269. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
  270. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
  271. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
  272. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  273. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  274. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  275. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  276. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
  277. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  278. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  279. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
  280. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
  281. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  282. claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
  283. claude_mpm/services/hook_service.py +106 -114
  284. claude_mpm/services/infrastructure/__init__.py +7 -5
  285. claude_mpm/services/infrastructure/context_preservation.py +233 -199
  286. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  287. claude_mpm/services/infrastructure/logging.py +83 -76
  288. claude_mpm/services/infrastructure/monitoring.py +547 -404
  289. claude_mpm/services/mcp_gateway/__init__.py +30 -13
  290. claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
  291. claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
  292. claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
  293. claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
  294. claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
  295. claude_mpm/services/mcp_gateway/core/base.py +80 -67
  296. claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
  297. claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
  298. claude_mpm/services/mcp_gateway/main.py +287 -137
  299. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  300. claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
  301. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  302. claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
  303. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
  304. claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
  305. claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
  306. claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
  307. claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
  308. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
  309. claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
  310. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
  311. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
  312. claude_mpm/services/memory/__init__.py +2 -2
  313. claude_mpm/services/memory/builder.py +451 -362
  314. claude_mpm/services/memory/cache/__init__.py +2 -2
  315. claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
  316. claude_mpm/services/memory/cache/simple_cache.py +107 -93
  317. claude_mpm/services/memory/indexed_memory.py +195 -193
  318. claude_mpm/services/memory/optimizer.py +267 -234
  319. claude_mpm/services/memory/router.py +571 -263
  320. claude_mpm/services/memory_hook_service.py +237 -0
  321. claude_mpm/services/port_manager.py +223 -0
  322. claude_mpm/services/project/__init__.py +3 -3
  323. claude_mpm/services/project/analyzer.py +451 -305
  324. claude_mpm/services/project/registry.py +262 -240
  325. claude_mpm/services/recovery_manager.py +287 -231
  326. claude_mpm/services/response_tracker.py +87 -67
  327. claude_mpm/services/runner_configuration_service.py +587 -0
  328. claude_mpm/services/session_management_service.py +304 -0
  329. claude_mpm/services/socketio/__init__.py +4 -4
  330. claude_mpm/services/socketio/client_proxy.py +174 -0
  331. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  332. claude_mpm/services/socketio/handlers/base.py +44 -30
  333. claude_mpm/services/socketio/handlers/connection.py +145 -65
  334. claude_mpm/services/socketio/handlers/file.py +123 -108
  335. claude_mpm/services/socketio/handlers/git.py +607 -373
  336. claude_mpm/services/socketio/handlers/hook.py +170 -0
  337. claude_mpm/services/socketio/handlers/memory.py +4 -4
  338. claude_mpm/services/socketio/handlers/project.py +4 -4
  339. claude_mpm/services/socketio/handlers/registry.py +53 -38
  340. claude_mpm/services/socketio/server/__init__.py +18 -0
  341. claude_mpm/services/socketio/server/broadcaster.py +252 -0
  342. claude_mpm/services/socketio/server/core.py +399 -0
  343. claude_mpm/services/socketio/server/main.py +323 -0
  344. claude_mpm/services/socketio_client_manager.py +160 -133
  345. claude_mpm/services/socketio_server.py +36 -1885
  346. claude_mpm/services/subprocess_launcher_service.py +316 -0
  347. claude_mpm/services/system_instructions_service.py +258 -0
  348. claude_mpm/services/ticket_manager.py +19 -533
  349. claude_mpm/services/utility_service.py +285 -0
  350. claude_mpm/services/version_control/__init__.py +18 -21
  351. claude_mpm/services/version_control/branch_strategy.py +20 -10
  352. claude_mpm/services/version_control/conflict_resolution.py +37 -13
  353. claude_mpm/services/version_control/git_operations.py +52 -21
  354. claude_mpm/services/version_control/semantic_versioning.py +92 -53
  355. claude_mpm/services/version_control/version_parser.py +145 -125
  356. claude_mpm/services/version_service.py +270 -0
  357. claude_mpm/storage/__init__.py +2 -2
  358. claude_mpm/storage/state_storage.py +177 -181
  359. claude_mpm/ticket_wrapper.py +2 -2
  360. claude_mpm/utils/__init__.py +2 -2
  361. claude_mpm/utils/agent_dependency_loader.py +453 -243
  362. claude_mpm/utils/config_manager.py +157 -118
  363. claude_mpm/utils/console.py +1 -1
  364. claude_mpm/utils/dependency_cache.py +102 -107
  365. claude_mpm/utils/dependency_manager.py +52 -47
  366. claude_mpm/utils/dependency_strategies.py +131 -96
  367. claude_mpm/utils/environment_context.py +110 -102
  368. claude_mpm/utils/error_handler.py +75 -55
  369. claude_mpm/utils/file_utils.py +80 -67
  370. claude_mpm/utils/framework_detection.py +12 -11
  371. claude_mpm/utils/import_migration_example.py +12 -60
  372. claude_mpm/utils/imports.py +48 -45
  373. claude_mpm/utils/path_operations.py +100 -93
  374. claude_mpm/utils/robust_installer.py +172 -164
  375. claude_mpm/utils/session_logging.py +30 -23
  376. claude_mpm/utils/subprocess_utils.py +99 -61
  377. claude_mpm/validation/__init__.py +1 -1
  378. claude_mpm/validation/agent_validator.py +151 -111
  379. claude_mpm/validation/frontmatter_validator.py +92 -71
  380. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +27 -1
  381. claude_mpm-4.0.3.dist-info/RECORD +402 -0
  382. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
  383. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
  384. claude_mpm/cli/commands/run_guarded.py +0 -511
  385. claude_mpm/config/memory_guardian_config.py +0 -325
  386. claude_mpm/config/memory_guardian_yaml.py +0 -335
  387. claude_mpm/core/config_paths.py +0 -150
  388. claude_mpm/core/memory_aware_runner.py +0 -353
  389. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  390. claude_mpm/deployment_paths.py +0 -261
  391. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  392. claude_mpm/models/state_models.py +0 -433
  393. claude_mpm/services/agent/__init__.py +0 -24
  394. claude_mpm/services/agent/deployment.py +0 -2548
  395. claude_mpm/services/agent/management.py +0 -598
  396. claude_mpm/services/agent/registry.py +0 -813
  397. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  398. claude_mpm/services/communication/socketio.py +0 -1935
  399. claude_mpm/services/communication/websocket.py +0 -479
  400. claude_mpm/services/framework_claude_md_generator.py +0 -624
  401. claude_mpm/services/health_monitor.py +0 -893
  402. claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
  403. claude_mpm/services/infrastructure/health_monitor.py +0 -775
  404. claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
  405. claude_mpm/services/infrastructure/memory_guardian.py +0 -944
  406. claude_mpm/services/infrastructure/restart_protection.py +0 -642
  407. claude_mpm/services/infrastructure/state_manager.py +0 -774
  408. claude_mpm/services/mcp_gateway/manager.py +0 -334
  409. claude_mpm/services/optimized_hook_service.py +0 -542
  410. claude_mpm/services/project_analyzer.py +0 -864
  411. claude_mpm/services/project_registry.py +0 -608
  412. claude_mpm/services/standalone_socketio_server.py +0 -1300
  413. claude_mpm/services/ticket_manager_di.py +0 -318
  414. claude_mpm/services/ticketing_service_original.py +0 -510
  415. claude_mpm/utils/paths.py +0 -395
  416. claude_mpm/utils/platform_memory.py +0 -524
  417. claude_mpm-3.9.11.dist-info/RECORD +0 -306
  418. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
  419. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,5 @@
1
+ from pathlib import Path
2
+
1
3
  #!/usr/bin/env python3
2
4
  """
3
5
  Agent Memory Manager Service
@@ -7,7 +9,7 @@ Manages agent memory files with size limits and validation.
7
9
 
8
10
  This service provides:
9
11
  - Memory file operations (load, save, validate)
10
- - Size limit enforcement (8KB default)
12
+ - Size limit enforcement (80KB default)
11
13
  - Auto-truncation when limits exceeded
12
14
  - Default memory template creation
13
15
  - Section management with item limits
@@ -18,57 +20,59 @@ Memory files are stored in .claude-mpm/memories/ directory
18
20
  following the naming convention: {agent_id}_agent.md
19
21
  """
20
22
 
21
- from pathlib import Path
22
- from typing import Dict, List, Optional, Any
23
- from datetime import datetime
24
- import re
25
23
  import logging
26
24
  import os
25
+ from datetime import datetime
26
+ from typing import Any, Dict, List, Optional, Tuple
27
27
 
28
28
  from claude_mpm.core.config import Config
29
- from claude_mpm.core.mixins import LoggerMixin
30
- from claude_mpm.utils.paths import PathResolver
31
- from claude_mpm.services.project_analyzer import ProjectAnalyzer
32
29
  from claude_mpm.core.interfaces import MemoryServiceInterface
33
- # Socket.IO notifications are optional - we'll skip them if server is not available
30
+ from claude_mpm.core.unified_paths import get_path_manager
31
+ from claude_mpm.services.project.analyzer import ProjectAnalyzer
32
+
33
+ from .analyzer import MemoryAnalyzer
34
+ from .content_manager import MemoryContentManager
35
+ from .template_generator import MemoryTemplateGenerator
34
36
 
35
37
 
36
38
  class AgentMemoryManager(MemoryServiceInterface):
37
39
  """Manages agent memory files with size limits and validation.
38
-
40
+
39
41
  WHY: Agents need to accumulate project-specific knowledge over time to become
40
42
  more effective. This service manages persistent memory files that agents can
41
43
  read before tasks and update with new learnings.
42
-
44
+
43
45
  DESIGN DECISION: Memory files are stored in .claude-mpm/memories/ (not project root)
44
46
  to keep them organized and separate from other project files. Files follow a
45
47
  standardized markdown format with enforced size limits to prevent unbounded growth.
46
-
48
+
47
49
  The 80KB limit (~20k tokens) balances comprehensive knowledge storage with
48
50
  reasonable context size for agent prompts.
49
51
  """
50
-
52
+
51
53
  # Default limits - will be overridden by configuration
52
54
  # Updated to support 20k tokens (~80KB) for enhanced memory capacity
53
55
  DEFAULT_MEMORY_LIMITS = {
54
- 'max_file_size_kb': 80, # Increased from 8KB to 80KB (20k tokens)
55
- 'max_sections': 10,
56
- 'max_items_per_section': 15,
57
- 'max_line_length': 120
56
+ "max_file_size_kb": 80, # Increased from 8KB to 80KB (20k tokens)
57
+ "max_sections": 10,
58
+ "max_items_per_section": 15,
59
+ "max_line_length": 120,
58
60
  }
59
-
61
+
60
62
  REQUIRED_SECTIONS = [
61
- 'Project Architecture',
62
- 'Implementation Guidelines',
63
- 'Common Mistakes to Avoid',
64
- 'Current Technical Context'
63
+ "Project Architecture",
64
+ "Implementation Guidelines",
65
+ "Common Mistakes to Avoid",
66
+ "Current Technical Context",
65
67
  ]
66
-
67
- def __init__(self, config: Optional[Config] = None, working_directory: Optional[Path] = None):
68
+
69
+ def __init__(
70
+ self, config: Optional[Config] = None, working_directory: Optional[Path] = None
71
+ ):
68
72
  """Initialize the memory manager.
69
-
73
+
70
74
  Sets up the memories directory and ensures it exists with proper README.
71
-
75
+
72
76
  Args:
73
77
  config: Optional Config object. If not provided, will create default Config.
74
78
  working_directory: Optional working directory. If not provided, uses current working directory.
@@ -76,20 +80,34 @@ class AgentMemoryManager(MemoryServiceInterface):
76
80
  # Initialize logger using the same pattern as LoggerMixin
77
81
  self._logger_instance = None
78
82
  self._logger_name = None
79
-
83
+
80
84
  self.config = config or Config()
81
- self.project_root = PathResolver.get_project_root()
85
+ self.project_root = get_path_manager().get_project_root()
82
86
  # Use current working directory by default, not project root
83
87
  self.working_directory = working_directory or Path(os.getcwd())
84
88
  self.memories_dir = self.working_directory / ".claude-mpm" / "memories"
85
89
  self._ensure_memories_directory()
86
-
90
+
87
91
  # Initialize memory limits from configuration
88
92
  self._init_memory_limits()
89
-
93
+
90
94
  # Initialize project analyzer for context-aware memory creation
91
95
  self.project_analyzer = ProjectAnalyzer(self.config, self.working_directory)
92
-
96
+
97
+ # Initialize component services
98
+ self.template_generator = MemoryTemplateGenerator(
99
+ self.config, self.working_directory, self.project_analyzer
100
+ )
101
+ self.content_manager = MemoryContentManager(self.memory_limits)
102
+ self.analyzer = MemoryAnalyzer(
103
+ self.memories_dir,
104
+ self.memory_limits,
105
+ self.agent_overrides,
106
+ self._get_agent_limits,
107
+ self._get_agent_auto_learning,
108
+ self.content_manager,
109
+ )
110
+
93
111
  @property
94
112
  def logger(self):
95
113
  """Get or create the logger instance (like LoggerMixin)."""
@@ -99,1258 +117,374 @@ class AgentMemoryManager(MemoryServiceInterface):
99
117
  else:
100
118
  module = self.__class__.__module__
101
119
  class_name = self.__class__.__name__
102
-
120
+
103
121
  if module and module != "__main__":
104
122
  logger_name = f"{module}.{class_name}"
105
123
  else:
106
124
  logger_name = class_name
107
-
125
+
108
126
  self._logger_instance = logging.getLogger(logger_name)
109
-
127
+
110
128
  return self._logger_instance
111
-
129
+
112
130
  def _init_memory_limits(self):
113
131
  """Initialize memory limits from configuration.
114
-
132
+
115
133
  WHY: Allows configuration-driven memory limits instead of hardcoded values.
116
134
  Supports agent-specific overrides for different memory requirements.
117
135
  """
118
136
  # Check if memory system is enabled
119
- self.memory_enabled = self.config.get('memory.enabled', True)
120
- self.auto_learning = self.config.get('memory.auto_learning', True) # Changed default to True
121
-
137
+ self.memory_enabled = self.config.get("memory.enabled", True)
138
+ self.auto_learning = self.config.get(
139
+ "memory.auto_learning", True
140
+ ) # Changed default to True
141
+
122
142
  # Load default limits from configuration
123
- config_limits = self.config.get('memory.limits', {})
143
+ config_limits = self.config.get("memory.limits", {})
124
144
  self.memory_limits = {
125
- 'max_file_size_kb': config_limits.get('default_size_kb',
126
- self.DEFAULT_MEMORY_LIMITS['max_file_size_kb']),
127
- 'max_sections': config_limits.get('max_sections',
128
- self.DEFAULT_MEMORY_LIMITS['max_sections']),
129
- 'max_items_per_section': config_limits.get('max_items_per_section',
130
- self.DEFAULT_MEMORY_LIMITS['max_items_per_section']),
131
- 'max_line_length': config_limits.get('max_line_length',
132
- self.DEFAULT_MEMORY_LIMITS['max_line_length'])
145
+ "max_file_size_kb": config_limits.get(
146
+ "default_size_kb", self.DEFAULT_MEMORY_LIMITS["max_file_size_kb"]
147
+ ),
148
+ "max_sections": config_limits.get(
149
+ "max_sections", self.DEFAULT_MEMORY_LIMITS["max_sections"]
150
+ ),
151
+ "max_items_per_section": config_limits.get(
152
+ "max_items_per_section",
153
+ self.DEFAULT_MEMORY_LIMITS["max_items_per_section"],
154
+ ),
155
+ "max_line_length": config_limits.get(
156
+ "max_line_length", self.DEFAULT_MEMORY_LIMITS["max_line_length"]
157
+ ),
133
158
  }
134
-
159
+
135
160
  # Load agent-specific overrides
136
- self.agent_overrides = self.config.get('memory.agent_overrides', {})
137
-
161
+ self.agent_overrides = self.config.get("memory.agent_overrides", {})
162
+
138
163
  def _get_agent_limits(self, agent_id: str) -> Dict[str, Any]:
139
164
  """Get memory limits for specific agent, including overrides.
140
-
165
+
141
166
  WHY: Different agents may need different memory capacities. Research agents
142
167
  might need larger memory for comprehensive findings, while simple agents
143
168
  can work with smaller limits.
144
-
169
+
145
170
  Args:
146
171
  agent_id: The agent identifier
147
-
172
+
148
173
  Returns:
149
174
  Dict containing the effective limits for this agent
150
175
  """
151
176
  # Start with default limits
152
177
  limits = self.memory_limits.copy()
153
-
178
+
154
179
  # Apply agent-specific overrides if they exist
155
180
  if agent_id in self.agent_overrides:
156
181
  overrides = self.agent_overrides[agent_id]
157
- if 'size_kb' in overrides:
158
- limits['max_file_size_kb'] = overrides['size_kb']
159
-
182
+ if "size_kb" in overrides:
183
+ limits["max_file_size_kb"] = overrides["size_kb"]
184
+
160
185
  return limits
161
-
186
+
162
187
  def _get_agent_auto_learning(self, agent_id: str) -> bool:
163
188
  """Check if auto-learning is enabled for specific agent.
164
-
189
+
165
190
  Args:
166
191
  agent_id: The agent identifier
167
-
192
+
168
193
  Returns:
169
194
  bool: True if auto-learning is enabled for this agent
170
195
  """
171
196
  # Check agent-specific override first
172
197
  if agent_id in self.agent_overrides:
173
- return self.agent_overrides[agent_id].get('auto_learning', self.auto_learning)
174
-
198
+ return self.agent_overrides[agent_id].get(
199
+ "auto_learning", self.auto_learning
200
+ )
201
+
175
202
  # Fall back to global setting
176
203
  return self.auto_learning
177
-
204
+
178
205
  def load_agent_memory(self, agent_id: str) -> str:
179
206
  """Load agent memory file content.
180
-
207
+
181
208
  WHY: Agents need to read their accumulated knowledge before starting tasks
182
209
  to apply learned patterns and avoid repeated mistakes.
183
-
210
+
184
211
  Args:
185
212
  agent_id: The agent identifier (e.g., 'research', 'engineer')
186
-
213
+
187
214
  Returns:
188
215
  str: The memory file content, creating default if doesn't exist
189
216
  """
190
217
  memory_file = self.memories_dir / f"{agent_id}_agent.md"
191
-
218
+
192
219
  if not memory_file.exists():
193
220
  self.logger.info(f"Creating default memory for agent: {agent_id}")
194
221
  return self._create_default_memory(agent_id)
195
-
222
+
196
223
  try:
197
- content = memory_file.read_text(encoding='utf-8')
198
-
199
- # Socket.IO notifications removed - memory manager works independently
200
-
201
- return self._validate_and_repair(content, agent_id)
224
+ content = memory_file.read_text(encoding="utf-8")
225
+ return self.content_manager.validate_and_repair(content, agent_id)
202
226
  except Exception as e:
203
227
  self.logger.error(f"Error reading memory file for {agent_id}: {e}")
204
228
  # Return default memory on error - never fail
205
229
  return self._create_default_memory(agent_id)
206
-
230
+
207
231
  def update_agent_memory(self, agent_id: str, section: str, new_item: str) -> bool:
208
232
  """Add new learning item to specified section.
209
-
233
+
210
234
  WHY: Agents discover new patterns and insights during task execution that
211
235
  should be preserved for future tasks. This method adds new learnings while
212
236
  enforcing size limits to prevent unbounded growth.
213
-
237
+
214
238
  Args:
215
239
  agent_id: The agent identifier
216
240
  section: The section name to add the item to
217
241
  new_item: The learning item to add
218
-
242
+
219
243
  Returns:
220
244
  bool: True if update succeeded, False otherwise
221
245
  """
222
246
  try:
223
247
  current_memory = self.load_agent_memory(agent_id)
224
- updated_memory = self._add_item_to_section(current_memory, section, new_item)
225
-
248
+ updated_memory = self.content_manager.add_item_to_section(
249
+ current_memory, section, new_item
250
+ )
251
+
226
252
  # Enforce limits
227
- if self._exceeds_limits(updated_memory, agent_id):
253
+ agent_limits = self._get_agent_limits(agent_id)
254
+ if self.content_manager.exceeds_limits(updated_memory, agent_limits):
228
255
  self.logger.debug(f"Memory for {agent_id} exceeds limits, truncating")
229
- updated_memory = self._truncate_to_limits(updated_memory, agent_id)
230
-
256
+ updated_memory = self.content_manager.truncate_to_limits(
257
+ updated_memory, agent_limits
258
+ )
259
+
231
260
  # Save with timestamp
232
261
  return self._save_memory_file(agent_id, updated_memory)
233
262
  except Exception as e:
234
263
  self.logger.error(f"Error updating memory for {agent_id}: {e}")
235
264
  # Never fail on memory errors
236
265
  return False
237
-
266
+
238
267
  def add_learning(self, agent_id: str, learning_type: str, content: str) -> bool:
239
268
  """Add structured learning to appropriate section.
240
-
269
+
241
270
  WHY: Different types of learnings belong in different sections for better
242
271
  organization and retrieval. This method maps learning types to appropriate
243
272
  sections automatically.
244
-
273
+
245
274
  Args:
246
275
  agent_id: The agent identifier
247
276
  learning_type: Type of learning (pattern, architecture, guideline, etc.)
248
277
  content: The learning content
249
-
278
+
250
279
  Returns:
251
280
  bool: True if learning was added successfully
252
281
  """
253
282
  section_mapping = {
254
- 'pattern': 'Coding Patterns Learned',
255
- 'architecture': 'Project Architecture',
256
- 'guideline': 'Implementation Guidelines',
257
- 'mistake': 'Common Mistakes to Avoid',
258
- 'strategy': 'Effective Strategies',
259
- 'integration': 'Integration Points',
260
- 'performance': 'Performance Considerations',
261
- 'domain': 'Domain-Specific Knowledge',
262
- 'context': 'Current Technical Context'
283
+ "pattern": "Coding Patterns Learned",
284
+ "architecture": "Project Architecture",
285
+ "guideline": "Implementation Guidelines",
286
+ "mistake": "Common Mistakes to Avoid",
287
+ "strategy": "Effective Strategies",
288
+ "integration": "Integration Points",
289
+ "performance": "Performance Considerations",
290
+ "domain": "Domain-Specific Knowledge",
291
+ "context": "Current Technical Context",
263
292
  }
264
-
265
- section = section_mapping.get(learning_type, 'Recent Learnings')
293
+
294
+ section = section_mapping.get(learning_type, "Recent Learnings")
266
295
  success = self.update_agent_memory(agent_id, section, content)
267
-
296
+
268
297
  # Socket.IO notifications removed - memory manager works independently
269
-
298
+
270
299
  return success
271
-
300
+
272
301
  def _create_default_memory(self, agent_id: str) -> str:
273
302
  """Create project-specific default memory file for agent.
274
-
303
+
275
304
  WHY: Instead of generic templates, agents need project-specific knowledge
276
305
  from the start. This analyzes the current project and creates contextual
277
306
  memories with actual project characteristics.
278
-
307
+
279
308
  Args:
280
309
  agent_id: The agent identifier
281
-
310
+
282
311
  Returns:
283
312
  str: The project-specific memory template content
284
313
  """
285
- # Convert agent_id to proper name, handling cases like "test_agent" -> "Test"
286
- agent_name = agent_id.replace('_agent', '').replace('_', ' ').title()
287
- timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
288
-
289
314
  # Get limits for this agent
290
315
  limits = self._get_agent_limits(agent_id)
291
-
292
- # Analyze the project for context-specific content
293
- try:
294
- project_characteristics = self.project_analyzer.analyze_project()
295
- project_context = self.project_analyzer.get_project_context_summary()
296
- important_files = self.project_analyzer.get_important_files_for_context()
297
-
298
- self.logger.info(f"Creating project-specific memory for {agent_id} using analyzed project context")
299
- except Exception as e:
300
- self.logger.warning(f"Error analyzing project for {agent_id}, falling back to basic template: {e}")
301
- return self._create_basic_memory_template(agent_id)
302
-
303
- # Create project-specific sections
304
- architecture_items = self._generate_architecture_section(project_characteristics)
305
- coding_patterns = self._generate_coding_patterns_section(project_characteristics)
306
- implementation_guidelines = self._generate_implementation_guidelines(project_characteristics)
307
- tech_context = self._generate_technical_context(project_characteristics)
308
- integration_points = self._generate_integration_points(project_characteristics)
309
-
310
- template = f"""# {agent_name} Agent Memory - {project_characteristics.project_name}
311
-
312
- <!-- MEMORY LIMITS: {limits['max_file_size_kb']}KB max | {limits['max_sections']} sections max | {limits['max_items_per_section']} items per section -->
313
- <!-- Last Updated: {timestamp} | Auto-updated by: {agent_id} -->
314
-
315
- ## Project Context
316
- {project_context}
317
-
318
- ## Project Architecture
319
- {self._format_section_items(architecture_items)}
320
-
321
- ## Coding Patterns Learned
322
- {self._format_section_items(coding_patterns)}
323
-
324
- ## Implementation Guidelines
325
- {self._format_section_items(implementation_guidelines)}
326
-
327
- ## Domain-Specific Knowledge
328
- <!-- Agent-specific knowledge for {project_characteristics.project_name} domain -->
329
- {self._generate_domain_knowledge_starters(project_characteristics, agent_id)}
330
-
331
- ## Effective Strategies
332
- <!-- Successful approaches discovered through experience -->
333
-
334
- ## Common Mistakes to Avoid
335
- {self._format_section_items(self._generate_common_mistakes(project_characteristics))}
336
-
337
- ## Integration Points
338
- {self._format_section_items(integration_points)}
339
316
 
340
- ## Performance Considerations
341
- {self._format_section_items(self._generate_performance_considerations(project_characteristics))}
317
+ # Delegate to template generator
318
+ template = self.template_generator.create_default_memory(agent_id, limits)
342
319
 
343
- ## Current Technical Context
344
- {self._format_section_items(tech_context)}
345
-
346
- ## Recent Learnings
347
- <!-- Most recent discoveries and insights -->
348
- """
349
-
350
320
  # Save default file
351
321
  try:
352
322
  memory_file = self.memories_dir / f"{agent_id}_agent.md"
353
- memory_file.write_text(template, encoding='utf-8')
323
+ memory_file.write_text(template, encoding="utf-8")
354
324
  self.logger.info(f"Created project-specific memory file for {agent_id}")
355
-
325
+
356
326
  except Exception as e:
357
327
  self.logger.error(f"Error saving default memory for {agent_id}: {e}")
358
-
359
- return template
360
-
361
- def _create_basic_memory_template(self, agent_id: str) -> str:
362
- """Create basic memory template when project analysis fails.
363
-
364
- WHY: Fallback template ensures agents always get some memory structure
365
- even if project analysis encounters errors.
366
-
367
- Args:
368
- agent_id: The agent identifier
369
-
370
- Returns:
371
- str: Basic memory template
372
- """
373
- agent_name = agent_id.replace('_agent', '').replace('_', ' ').title()
374
- project_name = self.project_root.name
375
- timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
376
- limits = self._get_agent_limits(agent_id)
377
-
378
- return f"""# {agent_name} Agent Memory - {project_name}
379
-
380
- <!-- MEMORY LIMITS: {limits['max_file_size_kb']}KB max | {limits['max_sections']} sections max | {limits['max_items_per_section']} items per section -->
381
- <!-- Last Updated: {timestamp} | Auto-updated by: {agent_id} -->
382
-
383
- ## Project Context
384
- {project_name}: Software project requiring analysis
385
-
386
- ## Project Architecture
387
- - Analyze project structure to understand architecture patterns
388
328
 
389
- ## Coding Patterns Learned
390
- - Observe codebase patterns and conventions during tasks
391
-
392
- ## Implementation Guidelines
393
- - Extract implementation guidelines from project documentation
394
-
395
- ## Domain-Specific Knowledge
396
- <!-- Agent-specific knowledge accumulates here -->
397
-
398
- ## Effective Strategies
399
- <!-- Successful approaches discovered through experience -->
400
-
401
- ## Common Mistakes to Avoid
402
- - Learn from errors encountered during project work
403
-
404
- ## Integration Points
405
- <!-- Key interfaces and integration patterns -->
406
-
407
- ## Performance Considerations
408
- <!-- Performance insights and optimization patterns -->
409
-
410
- ## Current Technical Context
411
- - Project analysis pending - gather context during tasks
329
+ return template
412
330
 
413
- ## Recent Learnings
414
- <!-- Most recent discoveries and insights -->
415
- """
416
-
417
- def _generate_architecture_section(self, characteristics) -> List[str]:
418
- """Generate architecture section items based on project analysis."""
419
- items = []
420
-
421
- # Architecture type
422
- items.append(f"{characteristics.architecture_type} with {characteristics.primary_language or 'mixed'} implementation")
423
-
424
- # Key directories structure
425
- if characteristics.key_directories:
426
- key_dirs = ", ".join(characteristics.key_directories[:5])
427
- items.append(f"Main directories: {key_dirs}")
428
-
429
- # Main modules
430
- if characteristics.main_modules:
431
- modules = ", ".join(characteristics.main_modules[:4])
432
- items.append(f"Core modules: {modules}")
433
-
434
- # Entry points
435
- if characteristics.entry_points:
436
- entries = ", ".join(characteristics.entry_points[:3])
437
- items.append(f"Entry points: {entries}")
438
-
439
- # Frameworks affecting architecture
440
- if characteristics.web_frameworks:
441
- frameworks = ", ".join(characteristics.web_frameworks[:3])
442
- items.append(f"Web framework stack: {frameworks}")
443
-
444
- return items[:8] # Limit to prevent overwhelming
445
-
446
- def _generate_coding_patterns_section(self, characteristics) -> List[str]:
447
- """Generate coding patterns section based on project analysis."""
448
- items = []
449
-
450
- # Language-specific patterns
451
- if characteristics.primary_language == 'python':
452
- items.append("Python project: use type hints, follow PEP 8 conventions")
453
- if 'django' in [fw.lower() for fw in characteristics.web_frameworks]:
454
- items.append("Django patterns: models, views, templates separation")
455
- elif 'flask' in [fw.lower() for fw in characteristics.web_frameworks]:
456
- items.append("Flask patterns: blueprint organization, app factory pattern")
457
- elif characteristics.primary_language == 'node_js':
458
- items.append("Node.js project: use async/await, ES6+ features")
459
- if 'express' in [fw.lower() for fw in characteristics.web_frameworks]:
460
- items.append("Express patterns: middleware usage, route organization")
461
-
462
- # Framework-specific patterns
463
- for framework in characteristics.frameworks[:3]:
464
- if 'react' in framework.lower():
465
- items.append("React patterns: component composition, hooks usage")
466
- elif 'vue' in framework.lower():
467
- items.append("Vue patterns: single file components, composition API")
468
-
469
- # Code conventions found
470
- for convention in characteristics.code_conventions[:3]:
471
- items.append(f"Project uses: {convention}")
472
-
473
- return items[:8]
474
-
475
- def _generate_implementation_guidelines(self, characteristics) -> List[str]:
476
- """Generate implementation guidelines based on project analysis."""
477
- items = []
478
-
479
- # Package manager guidance
480
- if characteristics.package_manager:
481
- items.append(f"Use {characteristics.package_manager} for dependency management")
482
-
483
- # Testing guidelines
484
- if characteristics.testing_framework:
485
- items.append(f"Write tests using {characteristics.testing_framework}")
486
-
487
- # Test patterns
488
- for pattern in characteristics.test_patterns[:2]:
489
- items.append(f"Follow {pattern.lower()}")
490
-
491
- # Build tools
492
- if characteristics.build_tools:
493
- tools = ", ".join(characteristics.build_tools[:2])
494
- items.append(f"Use build tools: {tools}")
495
-
496
- # Configuration patterns
497
- for config_pattern in characteristics.configuration_patterns[:2]:
498
- items.append(f"Configuration: {config_pattern}")
499
-
500
- # Important files to reference
501
- important_configs = characteristics.important_configs[:3]
502
- if important_configs:
503
- configs = ", ".join(important_configs)
504
- items.append(f"Key config files: {configs}")
505
-
506
- return items[:8]
507
-
508
- def _generate_technical_context(self, characteristics) -> List[str]:
509
- """Generate current technical context based on project analysis."""
510
- items = []
511
-
512
- # Technology stack summary
513
- tech_stack = []
514
- if characteristics.primary_language:
515
- tech_stack.append(characteristics.primary_language)
516
- tech_stack.extend(characteristics.frameworks[:2])
517
- if tech_stack:
518
- items.append(f"Tech stack: {', '.join(tech_stack)}")
519
-
520
- # Databases in use
521
- if characteristics.databases:
522
- dbs = ", ".join(characteristics.databases[:3])
523
- items.append(f"Data storage: {dbs}")
524
-
525
- # API patterns
526
- if characteristics.api_patterns:
527
- apis = ", ".join(characteristics.api_patterns[:2])
528
- items.append(f"API patterns: {apis}")
529
-
530
- # Key dependencies
531
- if characteristics.key_dependencies:
532
- deps = ", ".join(characteristics.key_dependencies[:4])
533
- items.append(f"Key dependencies: {deps}")
534
-
535
- # Documentation available
536
- if characteristics.documentation_files:
537
- docs = ", ".join(characteristics.documentation_files[:3])
538
- items.append(f"Documentation: {docs}")
539
-
540
- return items[:8]
541
-
542
- def _generate_integration_points(self, characteristics) -> List[str]:
543
- """Generate integration points based on project analysis."""
544
- items = []
545
-
546
- # Database integrations
547
- for db in characteristics.databases[:3]:
548
- items.append(f"{db.title()} database integration")
549
-
550
- # Web framework integrations
551
- for framework in characteristics.web_frameworks[:2]:
552
- items.append(f"{framework} web framework integration")
553
-
554
- # API integrations
555
- for api_pattern in characteristics.api_patterns[:2]:
556
- items.append(f"{api_pattern} integration pattern")
557
-
558
- # Common integration patterns based on dependencies
559
- integration_deps = [dep for dep in characteristics.key_dependencies
560
- if any(keyword in dep.lower() for keyword in ['redis', 'rabbit', 'celery', 'kafka', 'docker'])]
561
- for dep in integration_deps[:3]:
562
- items.append(f"{dep} integration")
563
-
564
- return items[:6]
565
-
566
- def _generate_common_mistakes(self, characteristics) -> List[str]:
567
- """Generate common mistakes based on project type and stack."""
568
- items = []
569
-
570
- # Language-specific mistakes
571
- if characteristics.primary_language == 'python':
572
- items.append("Avoid circular imports - use late imports when needed")
573
- items.append("Don't ignore virtual environment - always activate before work")
574
- elif characteristics.primary_language == 'node_js':
575
- items.append("Avoid callback hell - use async/await consistently")
576
- items.append("Don't commit node_modules - ensure .gitignore is correct")
577
-
578
- # Framework-specific mistakes
579
- if 'django' in [fw.lower() for fw in characteristics.web_frameworks]:
580
- items.append("Don't skip migrations - always create and apply them")
581
- elif 'flask' in [fw.lower() for fw in characteristics.web_frameworks]:
582
- items.append("Avoid app context issues - use proper application factory")
583
-
584
- # Database-specific mistakes
585
- if characteristics.databases:
586
- items.append("Don't ignore database transactions in multi-step operations")
587
- items.append("Avoid N+1 queries - use proper joins or prefetching")
588
-
589
- # Testing mistakes
590
- if characteristics.testing_framework:
591
- items.append("Don't skip test isolation - ensure tests can run independently")
592
-
593
- return items[:8]
594
-
595
- def _generate_performance_considerations(self, characteristics) -> List[str]:
596
- """Generate performance considerations based on project stack."""
597
- items = []
598
-
599
- # Language-specific performance
600
- if characteristics.primary_language == 'python':
601
- items.append("Use list comprehensions over loops where appropriate")
602
- items.append("Consider caching for expensive operations")
603
- elif characteristics.primary_language == 'node_js':
604
- items.append("Leverage event loop - avoid blocking operations")
605
- items.append("Use streams for large data processing")
606
-
607
- # Database performance
608
- if characteristics.databases:
609
- items.append("Index frequently queried columns")
610
- items.append("Use connection pooling for database connections")
611
-
612
- # Web framework performance
613
- if characteristics.web_frameworks:
614
- items.append("Implement appropriate caching strategies")
615
- items.append("Optimize static asset delivery")
616
-
617
- # Framework-specific performance
618
- if 'react' in [fw.lower() for fw in characteristics.frameworks]:
619
- items.append("Use React.memo for expensive component renders")
620
-
621
- return items[:6]
622
-
623
- def _generate_domain_knowledge_starters(self, characteristics, agent_id: str) -> str:
624
- """Generate domain-specific knowledge starters based on project and agent type."""
625
- items = []
626
-
627
- # Project terminology
628
- if characteristics.project_terminology:
629
- terms = ", ".join(characteristics.project_terminology[:4])
630
- items.append(f"- Key project terms: {terms}")
631
-
632
- # Agent-specific starters
633
- if 'research' in agent_id.lower():
634
- items.append("- Focus on code analysis, pattern discovery, and architectural insights")
635
- if characteristics.documentation_files:
636
- items.append("- Prioritize documentation analysis for comprehensive understanding")
637
- elif 'engineer' in agent_id.lower():
638
- items.append("- Focus on implementation patterns, coding standards, and best practices")
639
- if characteristics.testing_framework:
640
- items.append(f"- Ensure test coverage using {characteristics.testing_framework}")
641
- elif 'pm' in agent_id.lower() or 'manager' in agent_id.lower():
642
- items.append("- Focus on project coordination, task delegation, and progress tracking")
643
- items.append("- Monitor integration points and cross-component dependencies")
644
-
645
- return '\n'.join(items) if items else "<!-- Domain knowledge will accumulate here -->"
646
-
647
- def _format_section_items(self, items: List[str]) -> str:
648
- """Format list of items as markdown bullet points."""
649
- if not items:
650
- return "<!-- Items will be added as knowledge accumulates -->"
651
-
652
- formatted_items = []
653
- for item in items:
654
- # Ensure each item starts with a dash and is properly formatted
655
- if not item.startswith('- '):
656
- item = f"- {item}"
657
- formatted_items.append(item)
658
-
659
- return '\n'.join(formatted_items)
660
-
661
- def _add_item_to_section(self, content: str, section: str, new_item: str) -> str:
662
- """Add item to specified section, respecting limits.
663
-
664
- WHY: Each section has a maximum item limit to prevent information overload
665
- and maintain readability. When limits are reached, oldest items are removed
666
- to make room for new learnings (FIFO strategy).
667
-
668
- Args:
669
- content: Current memory file content
670
- section: Section name to add item to
671
- new_item: Item to add
672
-
673
- Returns:
674
- str: Updated content with new item added
675
- """
676
- lines = content.split('\n')
677
- section_start = None
678
- section_end = None
679
-
680
- # Find section boundaries
681
- for i, line in enumerate(lines):
682
- if line.startswith(f'## {section}'):
683
- section_start = i
684
- elif section_start is not None and line.startswith('## '):
685
- section_end = i
686
- break
687
-
688
- if section_start is None:
689
- # Section doesn't exist, add it
690
- return self._add_new_section(content, section, new_item)
691
-
692
- if section_end is None:
693
- section_end = len(lines)
694
-
695
- # Count existing items in section and find first item index
696
- item_count = 0
697
- first_item_index = None
698
- for i in range(section_start + 1, section_end):
699
- if lines[i].strip().startswith('- '):
700
- if first_item_index is None:
701
- first_item_index = i
702
- item_count += 1
703
-
704
- # Check if we can add more items
705
- if item_count >= self.memory_limits['max_items_per_section']:
706
- # Remove oldest item (first one) to make room
707
- if first_item_index is not None:
708
- lines.pop(first_item_index)
709
- section_end -= 1 # Adjust section end after removal
710
-
711
- # Add new item (find insertion point after any comments)
712
- insert_point = section_start + 1
713
- while insert_point < section_end and (
714
- not lines[insert_point].strip() or
715
- lines[insert_point].strip().startswith('<!--')
716
- ):
717
- insert_point += 1
718
-
719
- # Ensure line length limit (account for "- " prefix)
720
- max_item_length = self.memory_limits['max_line_length'] - 2 # Subtract 2 for "- " prefix
721
- if len(new_item) > max_item_length:
722
- new_item = new_item[:max_item_length - 3] + '...'
723
-
724
- lines.insert(insert_point, f"- {new_item}")
725
-
726
- # Update timestamp
727
- updated_content = '\n'.join(lines)
728
- return self._update_timestamp(updated_content)
729
-
730
- def _add_new_section(self, content: str, section: str, new_item: str) -> str:
731
- """Add a new section with the given item.
732
-
733
- WHY: When agents discover learnings that don't fit existing sections,
734
- we need to create new sections dynamically while respecting the maximum
735
- section limit.
736
-
737
- Args:
738
- content: Current memory content
739
- section: New section name
740
- new_item: First item for the section
741
-
742
- Returns:
743
- str: Updated content with new section
744
- """
745
- lines = content.split('\n')
746
-
747
- # Count existing sections
748
- section_count = sum(1 for line in lines if line.startswith('## '))
749
-
750
- if section_count >= self.memory_limits['max_sections']:
751
- self.logger.warning(f"Maximum sections reached, cannot add '{section}'")
752
- # Try to add to Recent Learnings instead
753
- return self._add_item_to_section(content, 'Recent Learnings', new_item)
754
-
755
- # Find insertion point (before Recent Learnings or at end)
756
- insert_point = len(lines)
757
- for i, line in enumerate(lines):
758
- if line.startswith('## Recent Learnings'):
759
- insert_point = i
760
- break
761
-
762
- # Insert new section
763
- new_section = [
764
- '',
765
- f'## {section}',
766
- f'- {new_item}',
767
- ''
768
- ]
769
-
770
- for j, line in enumerate(new_section):
771
- lines.insert(insert_point + j, line)
772
-
773
- return '\n'.join(lines)
774
-
775
- def _exceeds_limits(self, content: str, agent_id: Optional[str] = None) -> bool:
776
- """Check if content exceeds size limits.
777
-
778
- Args:
779
- content: Content to check
780
- agent_id: Optional agent ID for agent-specific limits
781
-
782
- Returns:
783
- bool: True if content exceeds limits
784
- """
785
- # Get appropriate limits based on agent
786
- if agent_id:
787
- limits = self._get_agent_limits(agent_id)
788
- else:
789
- limits = self.memory_limits
790
-
791
- size_kb = len(content.encode('utf-8')) / 1024
792
- return size_kb > limits['max_file_size_kb']
793
-
794
- def _truncate_to_limits(self, content: str, agent_id: Optional[str] = None) -> str:
795
- """Truncate content to fit within limits.
796
-
797
- WHY: When memory files exceed size limits, we need a strategy to reduce
798
- size while preserving the most important information. This implementation
799
- removes items from "Recent Learnings" first as they're typically less
800
- consolidated than other sections.
801
-
802
- Args:
803
- content: Content to truncate
804
-
805
- Returns:
806
- str: Truncated content within size limits
807
- """
808
- lines = content.split('\n')
809
-
810
- # Get appropriate limits based on agent
811
- if agent_id:
812
- limits = self._get_agent_limits(agent_id)
813
- else:
814
- limits = self.memory_limits
815
-
816
- # Strategy: Remove items from Recent Learnings first
817
- while self._exceeds_limits('\n'.join(lines), agent_id):
818
- removed = False
819
-
820
- # First try Recent Learnings
821
- for i, line in enumerate(lines):
822
- if line.startswith('## Recent Learnings'):
823
- # Find and remove first item in this section
824
- for j in range(i + 1, len(lines)):
825
- if lines[j].strip().startswith('- '):
826
- lines.pop(j)
827
- removed = True
828
- break
829
- elif lines[j].startswith('## '):
830
- break
831
- break
832
-
833
- # If no Recent Learnings items, remove from other sections
834
- if not removed:
835
- # Remove from sections in reverse order (bottom up)
836
- for i in range(len(lines) - 1, -1, -1):
837
- if lines[i].strip().startswith('- '):
838
- lines.pop(i)
839
- removed = True
840
- break
841
-
842
- # Safety: If nothing removed, truncate from end
843
- if not removed:
844
- lines = lines[:-10]
845
-
846
- return '\n'.join(lines)
847
-
848
- def _update_timestamp(self, content: str) -> str:
849
- """Update the timestamp in the file header.
850
-
851
- Args:
852
- content: Content to update
853
-
854
- Returns:
855
- str: Content with updated timestamp
856
- """
857
- timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
858
- return re.sub(
859
- r'<!-- Last Updated: .+ \| Auto-updated by: .+ -->',
860
- f'<!-- Last Updated: {timestamp} | Auto-updated by: system -->',
861
- content
862
- )
863
-
864
- def _validate_and_repair(self, content: str, agent_id: str) -> str:
865
- """Validate memory file and repair if needed.
866
-
867
- WHY: Memory files might be manually edited by developers or corrupted.
868
- This method ensures the file maintains required structure and sections.
869
-
870
- Args:
871
- content: Content to validate
872
- agent_id: Agent identifier
873
-
874
- Returns:
875
- str: Validated and repaired content
876
- """
877
- lines = content.split('\n')
878
- existing_sections = set()
879
-
880
- # Find existing sections
881
- for line in lines:
882
- if line.startswith('## '):
883
- section_name = line[3:].split('(')[0].strip()
884
- existing_sections.add(section_name)
885
-
886
- # Check for required sections
887
- missing_sections = []
888
- for required in self.REQUIRED_SECTIONS:
889
- if required not in existing_sections:
890
- missing_sections.append(required)
891
-
892
- if missing_sections:
893
- self.logger.info(f"Adding missing sections to {agent_id} memory: {missing_sections}")
894
-
895
- # Add missing sections before Recent Learnings
896
- insert_point = len(lines)
897
- for i, line in enumerate(lines):
898
- if line.startswith('## Recent Learnings'):
899
- insert_point = i
900
- break
901
-
902
- for section in missing_sections:
903
- section_content = [
904
- '',
905
- f'## {section}',
906
- '<!-- Section added by repair -->',
907
- ''
908
- ]
909
- for j, line in enumerate(section_content):
910
- lines.insert(insert_point + j, line)
911
- insert_point += len(section_content)
912
-
913
- return '\n'.join(lines)
914
-
915
331
  def _save_memory_file(self, agent_id: str, content: str) -> bool:
916
332
  """Save memory content to file.
917
-
333
+
918
334
  WHY: Memory updates need to be persisted atomically to prevent corruption
919
335
  and ensure learnings are preserved across agent invocations.
920
-
336
+
921
337
  Args:
922
338
  agent_id: Agent identifier
923
339
  content: Content to save
924
-
340
+
925
341
  Returns:
926
342
  bool: True if save succeeded
927
343
  """
928
344
  try:
929
345
  memory_file = self.memories_dir / f"{agent_id}_agent.md"
930
- memory_file.write_text(content, encoding='utf-8')
346
+ memory_file.write_text(content, encoding="utf-8")
931
347
  self.logger.debug(f"Saved memory for {agent_id}")
932
348
  return True
933
349
  except Exception as e:
934
350
  self.logger.error(f"Error saving memory for {agent_id}: {e}")
935
351
  return False
936
-
352
+
937
353
  def optimize_memory(self, agent_id: Optional[str] = None) -> Dict[str, Any]:
938
354
  """Optimize agent memory by consolidating/cleaning memories.
939
-
355
+
940
356
  WHY: Over time, memory files accumulate redundant or outdated information.
941
357
  This method delegates to the memory optimizer service to clean up and
942
358
  consolidate memories while preserving important information.
943
-
359
+
944
360
  Args:
945
361
  agent_id: Optional specific agent ID. If None, optimizes all agents.
946
-
362
+
947
363
  Returns:
948
364
  Dict containing optimization results and statistics
949
365
  """
950
366
  try:
951
367
  from claude_mpm.services.memory.optimizer import MemoryOptimizer
368
+
952
369
  optimizer = MemoryOptimizer(self.config, self.working_directory)
953
-
370
+
954
371
  if agent_id:
955
372
  result = optimizer.optimize_agent_memory(agent_id)
956
373
  self.logger.info(f"Optimized memory for agent: {agent_id}")
957
374
  else:
958
375
  result = optimizer.optimize_all_memories()
959
376
  self.logger.info("Optimized all agent memories")
960
-
377
+
961
378
  return result
962
379
  except Exception as e:
963
380
  self.logger.error(f"Error optimizing memory: {e}")
964
381
  return {"success": False, "error": str(e)}
965
-
382
+
966
383
  def build_memories_from_docs(self, force_rebuild: bool = False) -> Dict[str, Any]:
967
384
  """Build agent memories from project documentation.
968
-
385
+
969
386
  WHY: Project documentation contains valuable knowledge that should be
970
387
  extracted and assigned to appropriate agents for better context awareness.
971
-
388
+
972
389
  Args:
973
390
  force_rebuild: If True, rebuilds even if docs haven't changed
974
-
391
+
975
392
  Returns:
976
393
  Dict containing build results and statistics
977
394
  """
978
395
  try:
979
396
  from claude_mpm.services.memory.builder import MemoryBuilder
397
+
980
398
  builder = MemoryBuilder(self.config, self.working_directory)
981
-
399
+
982
400
  result = builder.build_from_documentation(force_rebuild)
983
401
  self.logger.info("Built memories from documentation")
984
-
402
+
985
403
  return result
986
404
  except Exception as e:
987
405
  self.logger.error(f"Error building memories from docs: {e}")
988
406
  return {"success": False, "error": str(e)}
989
-
990
- def route_memory_command(self, content: str, context: Optional[Dict] = None) -> Dict[str, Any]:
407
+
408
+ def route_memory_command(
409
+ self, content: str, context: Optional[Dict] = None
410
+ ) -> Dict[str, Any]:
991
411
  """Route memory command to appropriate agent via PM delegation.
992
-
412
+
993
413
  WHY: Memory commands like "remember this for next time" need to be analyzed
994
414
  to determine which agent should store the information. This method provides
995
415
  routing logic for PM agent delegation.
996
-
416
+
997
417
  Args:
998
418
  content: The content to be remembered
999
419
  context: Optional context for routing decisions
1000
-
420
+
1001
421
  Returns:
1002
422
  Dict containing routing decision and reasoning
1003
423
  """
1004
424
  try:
1005
425
  from claude_mpm.services.memory.router import MemoryRouter
426
+
1006
427
  router = MemoryRouter(self.config)
1007
-
428
+
1008
429
  routing_result = router.analyze_and_route(content, context)
1009
- self.logger.debug(f"Routed memory command: {routing_result['target_agent']}")
1010
-
430
+ self.logger.debug(
431
+ f"Routed memory command: {routing_result['target_agent']}"
432
+ )
433
+
1011
434
  return routing_result
1012
435
  except Exception as e:
1013
436
  self.logger.error(f"Error routing memory command: {e}")
1014
437
  return {"success": False, "error": str(e)}
1015
-
438
+
1016
439
  def get_memory_status(self) -> Dict[str, Any]:
1017
440
  """Get comprehensive memory system status.
1018
-
441
+
1019
442
  WHY: Provides detailed overview of memory system health, file sizes,
1020
443
  optimization opportunities, and agent-specific statistics for monitoring
1021
444
  and maintenance purposes.
1022
-
445
+
1023
446
  Returns:
1024
447
  Dict containing comprehensive memory system status
1025
448
  """
1026
- try:
1027
- status = {
1028
- "system_enabled": self.memory_enabled,
1029
- "auto_learning": self.auto_learning,
1030
- "memory_directory": str(self.memories_dir),
1031
- "total_agents": 0,
1032
- "total_size_kb": 0,
1033
- "agents": {},
1034
- "optimization_opportunities": [],
1035
- "system_health": "healthy"
1036
- }
1037
-
1038
- if not self.memories_dir.exists():
1039
- status["system_health"] = "no_memory_dir"
1040
- return status
1041
-
1042
- memory_files = list(self.memories_dir.glob("*_agent.md"))
1043
- status["total_agents"] = len(memory_files)
1044
-
1045
- total_size = 0
1046
- for file_path in memory_files:
1047
- stat = file_path.stat()
1048
- size_kb = stat.st_size / 1024
1049
- total_size += stat.st_size
1050
-
1051
- agent_id = file_path.stem.replace('_agent', '')
1052
- limits = self._get_agent_limits(agent_id)
1053
-
1054
- # Analyze file content
1055
- try:
1056
- content = file_path.read_text()
1057
- section_count = len([line for line in content.splitlines() if line.startswith('## ')])
1058
- learning_count = len([line for line in content.splitlines() if line.strip().startswith('- ')])
1059
-
1060
- agent_status = {
1061
- "size_kb": round(size_kb, 2),
1062
- "size_limit_kb": limits['max_file_size_kb'],
1063
- "size_utilization": min(100, round((size_kb / limits['max_file_size_kb']) * 100, 1)),
1064
- "sections": section_count,
1065
- "items": learning_count,
1066
- "last_modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
1067
- "auto_learning": self._get_agent_auto_learning(agent_id)
1068
- }
1069
-
1070
- # Check for optimization opportunities
1071
- if size_kb > limits['max_file_size_kb'] * 0.8:
1072
- status["optimization_opportunities"].append(f"{agent_id}: High memory usage ({size_kb:.1f}KB)")
1073
-
1074
- if section_count > limits['max_sections'] * 0.8:
1075
- status["optimization_opportunities"].append(f"{agent_id}: Many sections ({section_count})")
1076
-
1077
- status["agents"][agent_id] = agent_status
1078
-
1079
- except Exception as e:
1080
- status["agents"][agent_id] = {"error": str(e)}
1081
-
1082
- status["total_size_kb"] = round(total_size / 1024, 2)
1083
-
1084
- # Determine overall system health
1085
- if len(status["optimization_opportunities"]) > 3:
1086
- status["system_health"] = "needs_optimization"
1087
- elif status["total_size_kb"] > 100: # More than 100KB total
1088
- status["system_health"] = "high_usage"
1089
-
1090
- return status
1091
-
1092
- except Exception as e:
1093
- self.logger.error(f"Error getting memory status: {e}")
1094
- return {"success": False, "error": str(e)}
1095
-
449
+ return self.analyzer.get_memory_status()
450
+
1096
451
  def cross_reference_memories(self, query: Optional[str] = None) -> Dict[str, Any]:
1097
452
  """Find common patterns and cross-references across agent memories.
1098
-
453
+
1099
454
  WHY: Different agents may have learned similar or related information.
1100
455
  Cross-referencing helps identify knowledge gaps, redundancies, and
1101
456
  opportunities for knowledge sharing between agents.
1102
-
457
+
1103
458
  Args:
1104
459
  query: Optional query to filter cross-references
1105
-
460
+
1106
461
  Returns:
1107
462
  Dict containing cross-reference analysis results
1108
463
  """
1109
- try:
1110
- cross_refs = {
1111
- "common_patterns": [],
1112
- "knowledge_gaps": [],
1113
- "redundancies": [],
1114
- "agent_correlations": {},
1115
- "query_matches": [] if query else None
1116
- }
1117
-
1118
- if not self.memories_dir.exists():
1119
- return cross_refs
1120
-
1121
- memory_files = list(self.memories_dir.glob("*_agent.md"))
1122
- agent_memories = {}
1123
-
1124
- # Load all agent memories
1125
- for file_path in memory_files:
1126
- agent_id = file_path.stem.replace('_agent', '')
1127
- try:
1128
- content = file_path.read_text()
1129
- agent_memories[agent_id] = content
1130
- except Exception as e:
1131
- self.logger.warning(f"Error reading memory for {agent_id}: {e}")
1132
- continue
1133
-
1134
- # Find common patterns across agents
1135
- all_lines = []
1136
- agent_lines = {}
1137
-
1138
- for agent_id, content in agent_memories.items():
1139
- lines = [line.strip() for line in content.splitlines()
1140
- if line.strip().startswith('- ')]
1141
- agent_lines[agent_id] = lines
1142
- all_lines.extend([(line, agent_id) for line in lines])
1143
-
1144
- # Look for similar content (basic similarity check)
1145
- line_counts = {}
1146
- for line, agent_id in all_lines:
1147
- # Normalize line for comparison
1148
- normalized = line.lower().replace('- ', '').strip()
1149
- if len(normalized) > 20: # Only check substantial lines
1150
- if normalized not in line_counts:
1151
- line_counts[normalized] = []
1152
- line_counts[normalized].append(agent_id)
1153
-
1154
- # Find patterns appearing in multiple agents
1155
- for line, agents in line_counts.items():
1156
- if len(set(agents)) > 1: # Appears in multiple agents
1157
- cross_refs["common_patterns"].append({
1158
- "pattern": line[:100] + "..." if len(line) > 100 else line,
1159
- "agents": list(set(agents)),
1160
- "count": len(agents)
1161
- })
1162
-
1163
- # Query-specific matches
1164
- if query:
1165
- query_lower = query.lower()
1166
- for agent_id, content in agent_memories.items():
1167
- matches = []
1168
- for line in content.splitlines():
1169
- if query_lower in line.lower():
1170
- matches.append(line.strip())
1171
-
1172
- if matches:
1173
- cross_refs["query_matches"].append({
1174
- "agent": agent_id,
1175
- "matches": matches[:5] # Limit to first 5 matches
1176
- })
1177
-
1178
- # Calculate agent correlations (agents with similar knowledge domains)
1179
- for agent_a in agent_memories:
1180
- for agent_b in agent_memories:
1181
- if agent_a < agent_b: # Avoid duplicates
1182
- common_count = len([
1183
- line for line in line_counts.values()
1184
- if agent_a in line and agent_b in line
1185
- ])
1186
-
1187
- if common_count > 0:
1188
- correlation_key = f"{agent_a}+{agent_b}"
1189
- cross_refs["agent_correlations"][correlation_key] = common_count
1190
-
1191
- return cross_refs
1192
-
1193
- except Exception as e:
1194
- self.logger.error(f"Error cross-referencing memories: {e}")
1195
- return {"success": False, "error": str(e)}
464
+ return self.analyzer.cross_reference_memories(query)
1196
465
 
1197
466
  def get_all_memories_raw(self) -> Dict[str, Any]:
1198
467
  """Get all agent memories in structured JSON format.
1199
-
468
+
1200
469
  WHY: This provides programmatic access to all agent memories, allowing
1201
470
  external tools, scripts, or APIs to retrieve and process the complete
1202
471
  memory state of the system.
1203
-
1204
- DESIGN DECISION: Returns structured data with metadata for each agent
1205
- including file stats, sections, and parsed content. This enables both
1206
- content access and system analysis.
1207
-
472
+
1208
473
  Returns:
1209
474
  Dict containing structured memory data for all agents
1210
475
  """
1211
- try:
1212
- result = {
1213
- "success": True,
1214
- "timestamp": datetime.now().isoformat(),
1215
- "total_agents": 0,
1216
- "total_size_bytes": 0,
1217
- "agents": {}
1218
- }
1219
-
1220
- # Ensure directory exists
1221
- if not self.memories_dir.exists():
1222
- return {
1223
- "success": True,
1224
- "timestamp": datetime.now().isoformat(),
1225
- "total_agents": 0,
1226
- "total_size_bytes": 0,
1227
- "agents": {},
1228
- "message": "No memory directory found"
1229
- }
1230
-
1231
- # Find all agent memory files
1232
- memory_files = list(self.memories_dir.glob("*_agent.md"))
1233
- result["total_agents"] = len(memory_files)
1234
-
1235
- # Process each agent memory file
1236
- for file_path in sorted(memory_files):
1237
- agent_id = file_path.stem.replace('_agent', '')
1238
-
1239
- try:
1240
- # Get file stats
1241
- stat = file_path.stat()
1242
- file_size = stat.st_size
1243
- result["total_size_bytes"] += file_size
1244
-
1245
- # Load and parse memory content
1246
- memory_content = self.load_agent_memory(agent_id)
1247
-
1248
- if memory_content:
1249
- sections = self._parse_memory_content_to_dict(memory_content)
1250
-
1251
- # Count total items across all sections
1252
- total_items = sum(len(items) for items in sections.values())
1253
-
1254
- result["agents"][agent_id] = {
1255
- "agent_id": agent_id,
1256
- "file_path": str(file_path),
1257
- "file_size_bytes": file_size,
1258
- "file_size_kb": round(file_size / 1024, 2),
1259
- "last_modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
1260
- "sections_count": len(sections),
1261
- "total_items": total_items,
1262
- "auto_learning": self._get_agent_auto_learning(agent_id),
1263
- "size_limits": self._get_agent_limits(agent_id),
1264
- "sections": sections,
1265
- "raw_content": memory_content
1266
- }
1267
- else:
1268
- result["agents"][agent_id] = {
1269
- "agent_id": agent_id,
1270
- "file_path": str(file_path),
1271
- "file_size_bytes": file_size,
1272
- "file_size_kb": round(file_size / 1024, 2),
1273
- "last_modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
1274
- "error": "Could not load memory content",
1275
- "sections": {},
1276
- "raw_content": ""
1277
- }
1278
-
1279
- except Exception as e:
1280
- self.logger.error(f"Error processing memory for agent {agent_id}: {e}")
1281
- result["agents"][agent_id] = {
1282
- "agent_id": agent_id,
1283
- "file_path": str(file_path),
1284
- "error": str(e),
1285
- "sections": {},
1286
- "raw_content": ""
1287
- }
1288
-
1289
- result["total_size_kb"] = round(result["total_size_bytes"] / 1024, 2)
1290
- return result
1291
-
1292
- except Exception as e:
1293
- self.logger.error(f"Error getting all memories raw: {e}")
1294
- return {
1295
- "success": False,
1296
- "error": str(e),
1297
- "timestamp": datetime.now().isoformat()
1298
- }
1299
-
1300
- def _parse_memory_content_to_dict(self, content: str) -> Dict[str, List[str]]:
1301
- """Parse memory content into structured dictionary format.
1302
-
1303
- WHY: Provides consistent parsing of memory content into sections and items
1304
- for both display and programmatic access. This ensures the same parsing
1305
- logic is used across the system.
1306
-
1307
- Args:
1308
- content: Raw memory file content
1309
-
1310
- Returns:
1311
- Dict mapping section names to lists of items
1312
- """
1313
- sections = {}
1314
- current_section = None
1315
- current_items = []
1316
-
1317
- for line in content.split('\n'):
1318
- line = line.strip()
1319
-
1320
- # Skip empty lines and header information
1321
- if not line or line.startswith('#') and 'Memory Usage' in line:
1322
- continue
1323
-
1324
- if line.startswith('## ') and not line.startswith('## Memory Usage'):
1325
- # New section found
1326
- if current_section and current_items:
1327
- sections[current_section] = current_items.copy()
1328
-
1329
- current_section = line[3:].strip()
1330
- current_items = []
1331
-
1332
- elif line.startswith('- ') and current_section:
1333
- # Item in current section
1334
- item = line[2:].strip()
1335
- if item and len(item) > 3: # Filter out very short items
1336
- current_items.append(item)
1337
-
1338
- # Add final section
1339
- if current_section and current_items:
1340
- sections[current_section] = current_items
1341
-
1342
- return sections
476
+ return self.analyzer.get_all_memories_raw()
1343
477
 
1344
478
  def _ensure_memories_directory(self):
1345
479
  """Ensure memories directory exists with README.
1346
-
480
+
1347
481
  WHY: The memories directory needs clear documentation so developers
1348
482
  understand the purpose of these files and how to interact with them.
1349
483
  """
1350
484
  try:
1351
485
  self.memories_dir.mkdir(parents=True, exist_ok=True)
1352
486
  self.logger.debug(f"Ensured memories directory exists: {self.memories_dir}")
1353
-
487
+
1354
488
  readme_path = self.memories_dir / "README.md"
1355
489
  if not readme_path.exists():
1356
490
  readme_content = """# Agent Memory System
@@ -1361,7 +495,7 @@ Each agent maintains project-specific knowledge in these files. Agents read thei
1361
495
  ## Manual Editing
1362
496
  Feel free to edit these files to:
1363
497
  - Add project-specific guidelines
1364
- - Remove outdated information
498
+ - Remove outdated information
1365
499
  - Reorganize for better clarity
1366
500
  - Add domain-specific knowledge
1367
501
 
@@ -1390,27 +524,27 @@ Standard markdown with structured sections. Agents expect:
1390
524
  - Manually editable by developers
1391
525
  - Version controlled with project
1392
526
  """
1393
- readme_path.write_text(readme_content, encoding='utf-8')
527
+ readme_path.write_text(readme_content, encoding="utf-8")
1394
528
  self.logger.info("Created README.md in memories directory")
1395
-
529
+
1396
530
  except Exception as e:
1397
531
  self.logger.error(f"Error ensuring memories directory: {e}")
1398
532
  # Continue anyway - memory system should not block operations
1399
-
533
+
1400
534
  # ================================================================================
1401
535
  # Interface Adapter Methods
1402
536
  # ================================================================================
1403
537
  # These methods adapt the existing implementation to comply with MemoryServiceInterface
1404
-
538
+
1405
539
  def load_memory(self, agent_id: str) -> Optional[str]:
1406
540
  """Load memory for a specific agent.
1407
-
541
+
1408
542
  WHY: This adapter method provides interface compliance by wrapping
1409
543
  the existing load_agent_memory method.
1410
-
544
+
1411
545
  Args:
1412
546
  agent_id: Identifier of the agent
1413
-
547
+
1414
548
  Returns:
1415
549
  Memory content as string or None if not found
1416
550
  """
@@ -1420,152 +554,84 @@ Standard markdown with structured sections. Agents expect:
1420
554
  except Exception as e:
1421
555
  self.logger.error(f"Failed to load memory for {agent_id}: {e}")
1422
556
  return None
1423
-
557
+
1424
558
  def save_memory(self, agent_id: str, content: str) -> bool:
1425
559
  """Save memory for a specific agent.
1426
-
560
+
1427
561
  WHY: This adapter method provides interface compliance. The existing
1428
562
  implementation uses update_agent_memory for modifications, so we
1429
563
  implement a full save by writing directly to the file.
1430
-
564
+
1431
565
  Args:
1432
566
  agent_id: Identifier of the agent
1433
567
  content: Memory content to save
1434
-
568
+
1435
569
  Returns:
1436
570
  True if save successful
1437
571
  """
1438
572
  try:
1439
573
  memory_path = self.memories_dir / f"{agent_id}_agent.md"
1440
-
574
+
1441
575
  # Validate size before saving
1442
576
  is_valid, error_msg = self.validate_memory_size(content)
1443
577
  if not is_valid:
1444
578
  self.logger.error(f"Memory validation failed: {error_msg}")
1445
579
  return False
1446
-
580
+
1447
581
  # Write the content
1448
- memory_path.write_text(content, encoding='utf-8')
582
+ memory_path.write_text(content, encoding="utf-8")
1449
583
  self.logger.info(f"Saved memory for agent {agent_id}")
1450
584
  return True
1451
-
585
+
1452
586
  except Exception as e:
1453
587
  self.logger.error(f"Failed to save memory for {agent_id}: {e}")
1454
588
  return False
1455
-
1456
- def validate_memory_size(self, content: str) -> tuple[bool, Optional[str]]:
589
+
590
+ def validate_memory_size(self, content: str) -> Tuple[bool, Optional[str]]:
1457
591
  """Validate memory content size and structure.
1458
-
592
+
1459
593
  WHY: This adapter method provides interface compliance by implementing
1460
594
  validation based on configured limits.
1461
-
595
+
1462
596
  Args:
1463
597
  content: Memory content to validate
1464
-
598
+
1465
599
  Returns:
1466
600
  Tuple of (is_valid, error_message)
1467
601
  """
1468
- try:
1469
- # Check file size
1470
- size_kb = len(content.encode('utf-8')) / 1024
1471
- max_size_kb = self.memory_limits.get('max_file_size_kb', 8)
1472
-
1473
- if size_kb > max_size_kb:
1474
- return False, f"Memory size {size_kb:.1f}KB exceeds limit of {max_size_kb}KB"
1475
-
1476
- # Check section count
1477
- sections = re.findall(r'^##\s+(.+)$', content, re.MULTILINE)
1478
- max_sections = self.memory_limits.get('max_sections', 10)
1479
-
1480
- if len(sections) > max_sections:
1481
- return False, f"Too many sections: {len(sections)} (max {max_sections})"
1482
-
1483
- # Check for required sections
1484
- required = set(self.REQUIRED_SECTIONS)
1485
- found = set(sections)
1486
- missing = required - found
1487
-
1488
- if missing:
1489
- return False, f"Missing required sections: {', '.join(missing)}"
1490
-
1491
- return True, None
1492
-
1493
- except Exception as e:
1494
- return False, f"Validation error: {str(e)}"
1495
-
602
+ return self.content_manager.validate_memory_size(content)
603
+
1496
604
  def get_memory_metrics(self, agent_id: Optional[str] = None) -> Dict[str, Any]:
1497
605
  """Get memory usage metrics.
1498
-
606
+
1499
607
  WHY: This adapter method provides interface compliance by gathering
1500
608
  metrics about memory usage.
1501
-
609
+
1502
610
  Args:
1503
611
  agent_id: Optional specific agent ID, or None for all
1504
-
612
+
1505
613
  Returns:
1506
614
  Dictionary with memory metrics
1507
615
  """
1508
- metrics = {
1509
- "total_memories": 0,
1510
- "total_size_kb": 0,
1511
- "agent_metrics": {},
1512
- "limits": self.memory_limits.copy()
1513
- }
1514
-
1515
- try:
1516
- if agent_id:
1517
- # Get metrics for specific agent
1518
- memory_path = self.memories_dir / f"{agent_id}_agent.md"
1519
- if memory_path.exists():
1520
- content = memory_path.read_text(encoding='utf-8')
1521
- size_kb = len(content.encode('utf-8')) / 1024
1522
- sections = re.findall(r'^##\s+(.+)$', content, re.MULTILINE)
1523
-
1524
- metrics["agent_metrics"][agent_id] = {
1525
- "size_kb": round(size_kb, 2),
1526
- "sections": len(sections),
1527
- "exists": True
1528
- }
1529
- metrics["total_memories"] = 1
1530
- metrics["total_size_kb"] = round(size_kb, 2)
1531
- else:
1532
- # Get metrics for all agents
1533
- for memory_file in self.memories_dir.glob("*_agent.md"):
1534
- agent_name = memory_file.stem.replace("_agent", "")
1535
- content = memory_file.read_text(encoding='utf-8')
1536
- size_kb = len(content.encode('utf-8')) / 1024
1537
- sections = re.findall(r'^##\s+(.+)$', content, re.MULTILINE)
1538
-
1539
- metrics["agent_metrics"][agent_name] = {
1540
- "size_kb": round(size_kb, 2),
1541
- "sections": len(sections),
1542
- "exists": True
1543
- }
1544
- metrics["total_memories"] += 1
1545
- metrics["total_size_kb"] += size_kb
1546
-
1547
- metrics["total_size_kb"] = round(metrics["total_size_kb"], 2)
1548
-
1549
- except Exception as e:
1550
- self.logger.error(f"Failed to get memory metrics: {e}")
1551
-
1552
- return metrics
616
+ return self.analyzer.get_memory_metrics(agent_id)
1553
617
 
1554
618
 
1555
619
  # Convenience functions for external use
1556
- def get_memory_manager(config: Optional[Config] = None, working_directory: Optional[Path] = None) -> AgentMemoryManager:
620
+ def get_memory_manager(
621
+ config: Optional[Config] = None, working_directory: Optional[Path] = None
622
+ ) -> AgentMemoryManager:
1557
623
  """Get a singleton instance of the memory manager.
1558
-
624
+
1559
625
  WHY: The memory manager should be shared across the application to ensure
1560
626
  consistent file access and avoid multiple instances managing the same files.
1561
-
627
+
1562
628
  Args:
1563
629
  config: Optional Config object. Only used on first instantiation.
1564
630
  working_directory: Optional working directory. Only used on first instantiation.
1565
-
631
+
1566
632
  Returns:
1567
633
  AgentMemoryManager: The memory manager instance
1568
634
  """
1569
- if not hasattr(get_memory_manager, '_instance'):
635
+ if not hasattr(get_memory_manager, "_instance"):
1570
636
  get_memory_manager._instance = AgentMemoryManager(config, working_directory)
1571
- return get_memory_manager._instance
637
+ return get_memory_manager._instance