claude-mpm 3.9.9__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 (411) 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 +155 -0
  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 +90 -49
  31. claude_mpm/cli/__main__.py +3 -2
  32. claude_mpm/cli/commands/__init__.py +21 -18
  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 +143 -762
  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 -1150
  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 +217 -0
  69. claude_mpm/config/paths.py +94 -208
  70. claude_mpm/config/socketio_config.py +84 -73
  71. claude_mpm/constants.py +36 -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 +571 -0
  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 +40 -23
  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 +14 -21
  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 +97 -93
  298. claude_mpm/services/mcp_gateway/main.py +307 -127
  299. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  300. claude_mpm/services/mcp_gateway/registry/service_registry.py +100 -101
  301. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  302. claude_mpm/services/mcp_gateway/server/__init__.py +4 -4
  303. claude_mpm/services/mcp_gateway/server/{mcp_server.py → mcp_gateway.py} +149 -153
  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 +110 -121
  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 +20 -534
  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 +9 -0
  358. claude_mpm/storage/state_storage.py +552 -0
  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.9.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +51 -2
  381. claude_mpm-4.0.3.dist-info/RECORD +402 -0
  382. {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
  383. {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
  384. claude_mpm/config/memory_guardian_config.py +0 -325
  385. claude_mpm/core/config_paths.py +0 -150
  386. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  387. claude_mpm/deployment_paths.py +0 -261
  388. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  389. claude_mpm/models/state_models.py +0 -433
  390. claude_mpm/services/agent/__init__.py +0 -24
  391. claude_mpm/services/agent/deployment.py +0 -2548
  392. claude_mpm/services/agent/management.py +0 -598
  393. claude_mpm/services/agent/registry.py +0 -813
  394. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  395. claude_mpm/services/communication/socketio.py +0 -1935
  396. claude_mpm/services/communication/websocket.py +0 -479
  397. claude_mpm/services/framework_claude_md_generator.py +0 -624
  398. claude_mpm/services/health_monitor.py +0 -893
  399. claude_mpm/services/infrastructure/memory_guardian.py +0 -770
  400. claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +0 -444
  401. claude_mpm/services/optimized_hook_service.py +0 -542
  402. claude_mpm/services/project_analyzer.py +0 -864
  403. claude_mpm/services/project_registry.py +0 -608
  404. claude_mpm/services/standalone_socketio_server.py +0 -1300
  405. claude_mpm/services/ticket_manager_di.py +0 -318
  406. claude_mpm/services/ticketing_service_original.py +0 -510
  407. claude_mpm/utils/paths.py +0 -395
  408. claude_mpm/utils/platform_memory.py +0 -524
  409. claude_mpm-3.9.9.dist-info/RECORD +0 -293
  410. {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
  411. {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
claude_mpm/core/cache.py CHANGED
@@ -1,3 +1,5 @@
1
+ from pathlib import Path
2
+
1
3
  #!/usr/bin/env python3
2
4
  """File system caching with LRU eviction for performance optimization.
3
5
 
@@ -14,24 +16,23 @@ WHY file system caching:
14
16
  import asyncio
15
17
  import hashlib
16
18
  import json
17
- import os
18
19
  import pickle
19
20
  import threading
20
21
  import time
21
22
  from collections import OrderedDict
22
23
  from dataclasses import dataclass, field
23
24
  from datetime import datetime, timedelta
24
- from pathlib import Path
25
25
  from typing import Any, Callable, Dict, Optional, Tuple, TypeVar, Union
26
26
 
27
27
  from ..core.logger import get_logger
28
28
 
29
- T = TypeVar('T')
29
+ T = TypeVar("T")
30
30
 
31
31
 
32
32
  @dataclass
33
33
  class CacheEntry:
34
34
  """Single cache entry with metadata."""
35
+
35
36
  key: str
36
37
  value: Any
37
38
  size: int
@@ -39,14 +40,14 @@ class CacheEntry:
39
40
  last_accessed: datetime = field(default_factory=datetime.now)
40
41
  access_count: int = 0
41
42
  ttl: Optional[float] = None
42
-
43
+
43
44
  def is_expired(self) -> bool:
44
45
  """Check if entry has expired based on TTL."""
45
46
  if self.ttl is None:
46
47
  return False
47
48
  age = (datetime.now() - self.created_at).total_seconds()
48
49
  return age > self.ttl
49
-
50
+
50
51
  def touch(self):
51
52
  """Update last access time and increment counter."""
52
53
  self.last_accessed = datetime.now()
@@ -56,12 +57,13 @@ class CacheEntry:
56
57
  @dataclass
57
58
  class CacheStats:
58
59
  """Cache performance statistics."""
60
+
59
61
  hits: int = 0
60
62
  misses: int = 0
61
63
  evictions: int = 0
62
64
  total_size: int = 0
63
65
  entry_count: int = 0
64
-
66
+
65
67
  @property
66
68
  def hit_rate(self) -> float:
67
69
  """Calculate cache hit rate."""
@@ -71,19 +73,19 @@ class CacheStats:
71
73
 
72
74
  class FileSystemCache:
73
75
  """LRU cache for file system operations.
74
-
76
+
75
77
  WHY this design:
76
78
  - OrderedDict provides O(1) LRU operations
77
79
  - Thread-safe with fine-grained locking
78
80
  - Memory-aware with size limits
79
81
  - TTL support for dynamic content
80
-
82
+
81
83
  Example:
82
84
  cache = FileSystemCache(max_size_mb=100, default_ttl=300)
83
-
85
+
84
86
  # Cache file content
85
87
  content = cache.get_file('/path/to/file.json')
86
-
88
+
87
89
  # Cache expensive computation
88
90
  result = cache.get_or_compute(
89
91
  'expensive_key',
@@ -91,16 +93,16 @@ class FileSystemCache:
91
93
  ttl=60
92
94
  )
93
95
  """
94
-
96
+
95
97
  def __init__(
96
98
  self,
97
99
  max_size_mb: float = 100,
98
100
  max_entries: int = 10000,
99
101
  default_ttl: Optional[float] = None,
100
- persist_path: Optional[Path] = None
102
+ persist_path: Optional[Path] = None,
101
103
  ):
102
104
  """Initialize file system cache.
103
-
105
+
104
106
  Args:
105
107
  max_size_mb: Maximum cache size in megabytes
106
108
  max_entries: Maximum number of cache entries
@@ -111,16 +113,16 @@ class FileSystemCache:
111
113
  self.max_entries = max_entries
112
114
  self.default_ttl = default_ttl
113
115
  self.persist_path = persist_path
114
-
116
+
115
117
  self._cache: OrderedDict[str, CacheEntry] = OrderedDict()
116
118
  self._lock = threading.RLock()
117
119
  self._stats = CacheStats()
118
120
  self._logger = get_logger("fs_cache")
119
-
121
+
120
122
  # Load persisted cache if available
121
123
  if persist_path and persist_path.exists():
122
124
  self._load_cache()
123
-
125
+
124
126
  def _estimate_size(self, value: Any) -> int:
125
127
  """Estimate memory size of a value in bytes."""
126
128
  if isinstance(value, (str, bytes)):
@@ -137,13 +139,13 @@ class FileSystemCache:
137
139
  return len(pickle.dumps(value))
138
140
  except:
139
141
  return 100 # Default small size
140
-
142
+
141
143
  def _evict_lru(self):
142
144
  """Evict least recently used entries to make space."""
143
145
  with self._lock:
144
146
  while self._cache and (
145
- self._stats.total_size > self.max_size or
146
- self._stats.entry_count > self.max_entries
147
+ self._stats.total_size > self.max_size
148
+ or self._stats.entry_count > self.max_entries
147
149
  ):
148
150
  # Remove oldest entry (first in OrderedDict)
149
151
  key, entry = self._cache.popitem(last=False)
@@ -151,37 +153,36 @@ class FileSystemCache:
151
153
  self._stats.entry_count -= 1
152
154
  self._stats.evictions += 1
153
155
  self._logger.debug(f"Evicted cache entry: {key}")
154
-
156
+
155
157
  def _evict_expired(self):
156
158
  """Remove expired entries from cache."""
157
159
  with self._lock:
158
160
  expired_keys = [
159
- key for key, entry in self._cache.items()
160
- if entry.is_expired()
161
+ key for key, entry in self._cache.items() if entry.is_expired()
161
162
  ]
162
-
163
+
163
164
  for key in expired_keys:
164
165
  entry = self._cache.pop(key)
165
166
  self._stats.total_size -= entry.size
166
167
  self._stats.entry_count -= 1
167
168
  self._logger.debug(f"Expired cache entry: {key}")
168
-
169
+
169
170
  def get(self, key: str) -> Optional[Any]:
170
171
  """Get value from cache.
171
-
172
+
172
173
  Args:
173
174
  key: Cache key
174
-
175
+
175
176
  Returns:
176
177
  Cached value or None if not found/expired
177
178
  """
178
179
  with self._lock:
179
180
  entry = self._cache.get(key)
180
-
181
+
181
182
  if entry is None:
182
183
  self._stats.misses += 1
183
184
  return None
184
-
185
+
185
186
  # Check expiration
186
187
  if entry.is_expired():
187
188
  self._cache.pop(key)
@@ -189,70 +190,59 @@ class FileSystemCache:
189
190
  self._stats.entry_count -= 1
190
191
  self._stats.misses += 1
191
192
  return None
192
-
193
+
193
194
  # Update LRU order
194
195
  self._cache.move_to_end(key)
195
196
  entry.touch()
196
-
197
+
197
198
  self._stats.hits += 1
198
199
  return entry.value
199
-
200
- def put(
201
- self,
202
- key: str,
203
- value: Any,
204
- ttl: Optional[float] = None
205
- ) -> None:
200
+
201
+ def put(self, key: str, value: Any, ttl: Optional[float] = None) -> None:
206
202
  """Store value in cache.
207
-
203
+
208
204
  Args:
209
205
  key: Cache key
210
206
  value: Value to cache
211
207
  ttl: Time-to-live in seconds (overrides default)
212
208
  """
213
209
  size = self._estimate_size(value)
214
-
210
+
215
211
  # Don't cache if single item exceeds max size
216
212
  if size > self.max_size:
217
213
  self._logger.warning(f"Value too large to cache: {key} ({size} bytes)")
218
214
  return
219
-
215
+
220
216
  with self._lock:
221
217
  # Remove existing entry if present
222
218
  if key in self._cache:
223
219
  old_entry = self._cache.pop(key)
224
220
  self._stats.total_size -= old_entry.size
225
221
  self._stats.entry_count -= 1
226
-
222
+
227
223
  # Create new entry
228
224
  entry = CacheEntry(
229
- key=key,
230
- value=value,
231
- size=size,
232
- ttl=ttl or self.default_ttl
225
+ key=key, value=value, size=size, ttl=ttl or self.default_ttl
233
226
  )
234
-
227
+
235
228
  # Add to cache
236
229
  self._cache[key] = entry
237
230
  self._stats.total_size += size
238
231
  self._stats.entry_count += 1
239
-
232
+
240
233
  # Evict if necessary
241
234
  self._evict_lru()
242
-
235
+
243
236
  def get_or_compute(
244
- self,
245
- key: str,
246
- compute_fn: Callable[[], T],
247
- ttl: Optional[float] = None
237
+ self, key: str, compute_fn: Callable[[], T], ttl: Optional[float] = None
248
238
  ) -> T:
249
239
  """Get from cache or compute if missing.
250
-
240
+
251
241
  Args:
252
242
  key: Cache key
253
243
  compute_fn: Function to compute value if not cached
254
244
  ttl: Time-to-live in seconds
255
-
245
+
256
246
  Returns:
257
247
  Cached or computed value
258
248
  """
@@ -260,46 +250,46 @@ class FileSystemCache:
260
250
  value = self.get(key)
261
251
  if value is not None:
262
252
  return value
263
-
253
+
264
254
  # Compute value
265
255
  value = compute_fn()
266
-
256
+
267
257
  # Cache result
268
258
  self.put(key, value, ttl)
269
-
259
+
270
260
  return value
271
-
261
+
272
262
  def get_file(
273
263
  self,
274
264
  file_path: Union[str, Path],
275
- mode: str = 'r',
276
- encoding: str = 'utf-8',
277
- ttl: Optional[float] = None
265
+ mode: str = "r",
266
+ encoding: str = "utf-8",
267
+ ttl: Optional[float] = None,
278
268
  ) -> Optional[Any]:
279
269
  """Get file content from cache or read from disk.
280
-
270
+
281
271
  Args:
282
272
  file_path: Path to file
283
273
  mode: File open mode ('r' for text, 'rb' for binary)
284
274
  encoding: Text encoding (for text mode)
285
275
  ttl: Time-to-live in seconds
286
-
276
+
287
277
  Returns:
288
278
  File content or None if file doesn't exist
289
279
  """
290
280
  file_path = Path(file_path)
291
-
281
+
292
282
  # Generate cache key based on file path and modification time
293
283
  if file_path.exists():
294
284
  mtime = file_path.stat().st_mtime
295
285
  cache_key = f"file:{file_path}:{mtime}:{mode}"
296
286
  else:
297
287
  return None
298
-
288
+
299
289
  def read_file():
300
290
  """Read file from disk."""
301
291
  try:
302
- if 'b' in mode:
292
+ if "b" in mode:
303
293
  with open(file_path, mode) as f:
304
294
  return f.read()
305
295
  else:
@@ -308,39 +298,37 @@ class FileSystemCache:
308
298
  except Exception as e:
309
299
  self._logger.error(f"Failed to read file {file_path}: {e}")
310
300
  return None
311
-
301
+
312
302
  return self.get_or_compute(cache_key, read_file, ttl)
313
-
303
+
314
304
  def get_json(
315
- self,
316
- file_path: Union[str, Path],
317
- ttl: Optional[float] = None
305
+ self, file_path: Union[str, Path], ttl: Optional[float] = None
318
306
  ) -> Optional[Dict[str, Any]]:
319
307
  """Get JSON file content from cache or parse from disk.
320
-
308
+
321
309
  Args:
322
310
  file_path: Path to JSON file
323
311
  ttl: Time-to-live in seconds
324
-
312
+
325
313
  Returns:
326
314
  Parsed JSON or None if file doesn't exist/invalid
327
315
  """
328
- content = self.get_file(file_path, mode='r', ttl=ttl)
316
+ content = self.get_file(file_path, mode="r", ttl=ttl)
329
317
  if content is None:
330
318
  return None
331
-
319
+
332
320
  try:
333
321
  return json.loads(content)
334
322
  except json.JSONDecodeError as e:
335
323
  self._logger.error(f"Invalid JSON in {file_path}: {e}")
336
324
  return None
337
-
325
+
338
326
  def invalidate(self, key: str) -> bool:
339
327
  """Remove entry from cache.
340
-
328
+
341
329
  Args:
342
330
  key: Cache key to invalidate
343
-
331
+
344
332
  Returns:
345
333
  True if entry was removed, False if not found
346
334
  """
@@ -351,44 +339,43 @@ class FileSystemCache:
351
339
  self._stats.entry_count -= 1
352
340
  return True
353
341
  return False
354
-
342
+
355
343
  def invalidate_pattern(self, pattern: str) -> int:
356
344
  """Invalidate all keys matching pattern.
357
-
345
+
358
346
  Args:
359
347
  pattern: Pattern to match (supports * wildcard)
360
-
348
+
361
349
  Returns:
362
350
  Number of entries invalidated
363
351
  """
364
352
  import fnmatch
365
-
353
+
366
354
  with self._lock:
367
355
  matching_keys = [
368
- key for key in self._cache.keys()
369
- if fnmatch.fnmatch(key, pattern)
356
+ key for key in self._cache.keys() if fnmatch.fnmatch(key, pattern)
370
357
  ]
371
-
358
+
372
359
  count = 0
373
360
  for key in matching_keys:
374
361
  if self.invalidate(key):
375
362
  count += 1
376
-
363
+
377
364
  return count
378
-
365
+
379
366
  def clear(self):
380
367
  """Clear all cache entries."""
381
368
  with self._lock:
382
369
  self._cache.clear()
383
370
  self._stats = CacheStats()
384
371
  self._logger.info("Cache cleared")
385
-
372
+
386
373
  def get_stats(self) -> Dict[str, Any]:
387
374
  """Get cache statistics."""
388
375
  with self._lock:
389
376
  # Clean up expired entries first
390
377
  self._evict_expired()
391
-
378
+
392
379
  return {
393
380
  "hits": self._stats.hits,
394
381
  "misses": self._stats.misses,
@@ -396,38 +383,38 @@ class FileSystemCache:
396
383
  "evictions": self._stats.evictions,
397
384
  "entry_count": self._stats.entry_count,
398
385
  "total_size_mb": self._stats.total_size / (1024 * 1024),
399
- "max_size_mb": self.max_size / (1024 * 1024)
386
+ "max_size_mb": self.max_size / (1024 * 1024),
400
387
  }
401
-
388
+
402
389
  def _save_cache(self):
403
390
  """Persist cache to disk."""
404
391
  if not self.persist_path:
405
392
  return
406
-
393
+
407
394
  try:
408
395
  self.persist_path.parent.mkdir(parents=True, exist_ok=True)
409
- with open(self.persist_path, 'wb') as f:
396
+ with open(self.persist_path, "wb") as f:
410
397
  pickle.dump(self._cache, f)
411
398
  self._logger.debug(f"Cache persisted to {self.persist_path}")
412
399
  except Exception as e:
413
400
  self._logger.error(f"Failed to persist cache: {e}")
414
-
401
+
415
402
  def _load_cache(self):
416
403
  """Load cache from disk."""
417
404
  if not self.persist_path or not self.persist_path.exists():
418
405
  return
419
-
406
+
420
407
  try:
421
- with open(self.persist_path, 'rb') as f:
408
+ with open(self.persist_path, "rb") as f:
422
409
  loaded_cache = pickle.load(f)
423
-
410
+
424
411
  # Rebuild cache with validation
425
412
  for key, entry in loaded_cache.items():
426
413
  if not entry.is_expired():
427
414
  self._cache[key] = entry
428
415
  self._stats.total_size += entry.size
429
416
  self._stats.entry_count += 1
430
-
417
+
431
418
  self._logger.info(f"Loaded {len(self._cache)} entries from cache")
432
419
  except Exception as e:
433
420
  self._logger.error(f"Failed to load cache: {e}")
@@ -435,82 +422,67 @@ class FileSystemCache:
435
422
 
436
423
  class AsyncFileSystemCache:
437
424
  """Async version of FileSystemCache for async applications.
438
-
425
+
439
426
  Provides non-blocking cache operations for async contexts.
440
427
  """
441
-
428
+
442
429
  def __init__(
443
430
  self,
444
431
  max_size_mb: float = 100,
445
432
  max_entries: int = 10000,
446
- default_ttl: Optional[float] = None
433
+ default_ttl: Optional[float] = None,
447
434
  ):
448
435
  self.sync_cache = FileSystemCache(
449
- max_size_mb=max_size_mb,
450
- max_entries=max_entries,
451
- default_ttl=default_ttl
436
+ max_size_mb=max_size_mb, max_entries=max_entries, default_ttl=default_ttl
452
437
  )
453
438
  self._lock = asyncio.Lock()
454
439
  self._logger = get_logger("async_fs_cache")
455
-
440
+
456
441
  async def get(self, key: str) -> Optional[Any]:
457
442
  """Get value from cache asynchronously."""
458
443
  async with self._lock:
459
444
  # Run in executor to avoid blocking
460
445
  loop = asyncio.get_event_loop()
461
446
  return await loop.run_in_executor(None, self.sync_cache.get, key)
462
-
463
- async def put(
464
- self,
465
- key: str,
466
- value: Any,
467
- ttl: Optional[float] = None
468
- ) -> None:
447
+
448
+ async def put(self, key: str, value: Any, ttl: Optional[float] = None) -> None:
469
449
  """Store value in cache asynchronously."""
470
450
  async with self._lock:
471
451
  loop = asyncio.get_event_loop()
472
452
  await loop.run_in_executor(None, self.sync_cache.put, key, value, ttl)
473
-
453
+
474
454
  async def get_or_compute(
475
- self,
476
- key: str,
477
- compute_fn: Callable[[], T],
478
- ttl: Optional[float] = None
455
+ self, key: str, compute_fn: Callable[[], T], ttl: Optional[float] = None
479
456
  ) -> T:
480
457
  """Get from cache or compute asynchronously."""
481
458
  # Try cache first
482
459
  value = await self.get(key)
483
460
  if value is not None:
484
461
  return value
485
-
462
+
486
463
  # Compute value (handle both sync and async functions)
487
464
  if asyncio.iscoroutinefunction(compute_fn):
488
465
  value = await compute_fn()
489
466
  else:
490
467
  loop = asyncio.get_event_loop()
491
468
  value = await loop.run_in_executor(None, compute_fn)
492
-
469
+
493
470
  # Cache result
494
471
  await self.put(key, value, ttl)
495
-
472
+
496
473
  return value
497
-
474
+
498
475
  async def get_file(
499
476
  self,
500
477
  file_path: Union[str, Path],
501
- mode: str = 'r',
502
- encoding: str = 'utf-8',
503
- ttl: Optional[float] = None
478
+ mode: str = "r",
479
+ encoding: str = "utf-8",
480
+ ttl: Optional[float] = None,
504
481
  ) -> Optional[Any]:
505
482
  """Get file content asynchronously."""
506
483
  loop = asyncio.get_event_loop()
507
484
  return await loop.run_in_executor(
508
- None,
509
- self.sync_cache.get_file,
510
- file_path,
511
- mode,
512
- encoding,
513
- ttl
485
+ None, self.sync_cache.get_file, file_path, mode, encoding, ttl
514
486
  )
515
487
 
516
488
 
@@ -520,15 +492,14 @@ _async_cache: Optional[AsyncFileSystemCache] = None
520
492
 
521
493
 
522
494
  def get_file_cache(
523
- max_size_mb: float = 100,
524
- default_ttl: Optional[float] = 300
495
+ max_size_mb: float = 100, default_ttl: Optional[float] = 300
525
496
  ) -> FileSystemCache:
526
497
  """Get or create global file cache instance.
527
-
498
+
528
499
  Args:
529
500
  max_size_mb: Maximum cache size in MB
530
501
  default_ttl: Default TTL in seconds
531
-
502
+
532
503
  Returns:
533
504
  Global FileSystemCache instance
534
505
  """
@@ -537,60 +508,55 @@ def get_file_cache(
537
508
  # Use project cache directory
538
509
  cache_dir = Path.home() / ".claude-mpm" / "cache"
539
510
  cache_dir.mkdir(parents=True, exist_ok=True)
540
-
511
+
541
512
  _file_cache = FileSystemCache(
542
513
  max_size_mb=max_size_mb,
543
514
  default_ttl=default_ttl,
544
- persist_path=cache_dir / "fs_cache.pkl"
515
+ persist_path=cache_dir / "fs_cache.pkl",
545
516
  )
546
517
  return _file_cache
547
518
 
548
519
 
549
520
  def get_async_cache(
550
- max_size_mb: float = 100,
551
- default_ttl: Optional[float] = 300
521
+ max_size_mb: float = 100, default_ttl: Optional[float] = 300
552
522
  ) -> AsyncFileSystemCache:
553
523
  """Get or create global async cache instance."""
554
524
  global _async_cache
555
525
  if _async_cache is None:
556
526
  _async_cache = AsyncFileSystemCache(
557
- max_size_mb=max_size_mb,
558
- default_ttl=default_ttl
527
+ max_size_mb=max_size_mb, default_ttl=default_ttl
559
528
  )
560
529
  return _async_cache
561
530
 
562
531
 
563
- def cache_decorator(
564
- ttl: Optional[float] = None,
565
- key_prefix: str = ""
566
- ):
532
+ def cache_decorator(ttl: Optional[float] = None, key_prefix: str = ""):
567
533
  """Decorator for caching function results.
568
-
534
+
569
535
  Args:
570
536
  ttl: Time-to-live in seconds
571
537
  key_prefix: Optional prefix for cache keys
572
-
538
+
573
539
  Example:
574
540
  @cache_decorator(ttl=60)
575
541
  def expensive_function(param):
576
542
  return compute_result(param)
577
543
  """
544
+
578
545
  def decorator(func: Callable) -> Callable:
579
546
  cache = get_file_cache()
580
-
547
+
581
548
  def wrapper(*args, **kwargs):
582
549
  # Generate cache key from function name and arguments
583
550
  key_parts = [key_prefix, func.__name__]
584
551
  key_parts.extend(str(arg) for arg in args)
585
552
  key_parts.extend(f"{k}={v}" for k, v in sorted(kwargs.items()))
586
553
  cache_key = ":".join(key_parts)
587
-
554
+
588
555
  # Use cache
589
556
  return cache.get_or_compute(
590
- cache_key,
591
- lambda: func(*args, **kwargs),
592
- ttl=ttl
557
+ cache_key, lambda: func(*args, **kwargs), ttl=ttl
593
558
  )
594
-
559
+
595
560
  return wrapper
596
- return decorator
561
+
562
+ return decorator