claude-mpm 3.9.11__py3-none-any.whl → 4.0.4__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 (434) 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 +2 -2
  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 +330 -86
  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 +363 -220
  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 +124 -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/built/components/agent-inference.js +2 -0
  113. claude_mpm/dashboard/static/built/components/event-processor.js +2 -0
  114. claude_mpm/dashboard/static/built/components/event-viewer.js +2 -0
  115. claude_mpm/dashboard/static/built/components/export-manager.js +2 -0
  116. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +2 -0
  117. claude_mpm/dashboard/static/built/components/hud-library-loader.js +2 -0
  118. claude_mpm/dashboard/static/built/components/hud-manager.js +2 -0
  119. claude_mpm/dashboard/static/built/components/hud-visualizer.js +2 -0
  120. claude_mpm/dashboard/static/built/components/module-viewer.js +2 -0
  121. claude_mpm/dashboard/static/built/components/session-manager.js +2 -0
  122. claude_mpm/dashboard/static/built/components/socket-manager.js +2 -0
  123. claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -0
  124. claude_mpm/dashboard/static/built/components/working-directory.js +2 -0
  125. claude_mpm/dashboard/static/built/dashboard.js +2 -0
  126. claude_mpm/dashboard/static/built/socket-client.js +2 -0
  127. claude_mpm/dashboard/static/css/dashboard.css +27 -8
  128. claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
  129. claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
  130. claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
  131. claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
  132. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
  133. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
  134. claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
  135. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
  136. claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
  137. claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
  138. claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
  139. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
  140. claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
  141. claude_mpm/dashboard/static/dist/dashboard.js +2 -0
  142. claude_mpm/dashboard/static/dist/socket-client.js +2 -0
  143. claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
  144. claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
  145. claude_mpm/dashboard/static/js/components/event-viewer.js +93 -72
  146. claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
  147. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +110 -96
  148. claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
  149. claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
  150. claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
  151. claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
  152. claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
  153. claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
  154. claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
  155. claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
  156. claude_mpm/dashboard/static/js/dashboard.js +178 -453
  157. claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
  158. claude_mpm/dashboard/static/js/socket-client.js +133 -53
  159. claude_mpm/dashboard/templates/index.html +40 -50
  160. claude_mpm/experimental/cli_enhancements.py +60 -58
  161. claude_mpm/generators/__init__.py +1 -1
  162. claude_mpm/generators/agent_profile_generator.py +75 -65
  163. claude_mpm/hooks/__init__.py +1 -1
  164. claude_mpm/hooks/base_hook.py +33 -28
  165. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  166. claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
  167. claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
  168. claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
  169. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
  170. claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
  171. claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
  172. claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
  173. claude_mpm/hooks/memory_integration_hook.py +140 -100
  174. claude_mpm/hooks/tool_call_interceptor.py +89 -76
  175. claude_mpm/hooks/validation_hooks.py +57 -49
  176. claude_mpm/init.py +145 -121
  177. claude_mpm/models/__init__.py +9 -9
  178. claude_mpm/models/agent_definition.py +33 -23
  179. claude_mpm/models/agent_session.py +228 -200
  180. claude_mpm/scripts/__init__.py +1 -1
  181. claude_mpm/scripts/socketio_daemon.py +192 -75
  182. claude_mpm/scripts/socketio_server_manager.py +328 -0
  183. claude_mpm/scripts/start_activity_logging.py +25 -22
  184. claude_mpm/services/__init__.py +68 -43
  185. claude_mpm/services/agent_capabilities_service.py +271 -0
  186. claude_mpm/services/agents/__init__.py +23 -32
  187. claude_mpm/services/agents/deployment/__init__.py +3 -3
  188. claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
  189. claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
  190. claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
  191. claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
  192. claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
  193. claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
  194. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
  195. claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
  196. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
  197. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
  198. claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
  199. claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
  200. claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
  201. claude_mpm/services/agents/deployment/agent_validator.py +352 -0
  202. claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
  203. claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
  204. claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
  205. claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
  206. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  207. claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
  208. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  209. claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
  210. claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
  211. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  212. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  213. claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
  214. claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
  215. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  216. claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
  217. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  218. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  219. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  220. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  221. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
  222. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  223. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  224. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
  225. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
  226. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
  227. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
  228. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
  229. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  230. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
  231. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  232. claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
  233. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
  234. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  235. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  236. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  237. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  238. claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
  239. claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
  240. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  241. claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
  242. claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
  243. claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
  244. claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
  245. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  246. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  247. claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
  248. claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
  249. claude_mpm/services/agents/loading/__init__.py +2 -2
  250. claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
  251. claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
  252. claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
  253. claude_mpm/services/agents/management/__init__.py +2 -2
  254. claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
  255. claude_mpm/services/agents/management/agent_management_service.py +209 -156
  256. claude_mpm/services/agents/memory/__init__.py +9 -6
  257. claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
  258. claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
  259. claude_mpm/services/agents/memory/analyzer.py +430 -0
  260. claude_mpm/services/agents/memory/content_manager.py +376 -0
  261. claude_mpm/services/agents/memory/template_generator.py +468 -0
  262. claude_mpm/services/agents/registry/__init__.py +7 -10
  263. claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
  264. claude_mpm/services/agents/registry/modification_tracker.py +351 -285
  265. claude_mpm/services/async_session_logger.py +187 -153
  266. claude_mpm/services/claude_session_logger.py +87 -72
  267. claude_mpm/services/command_handler_service.py +217 -0
  268. claude_mpm/services/communication/__init__.py +3 -2
  269. claude_mpm/services/core/__init__.py +50 -97
  270. claude_mpm/services/core/base.py +60 -53
  271. claude_mpm/services/core/interfaces/__init__.py +188 -0
  272. claude_mpm/services/core/interfaces/agent.py +351 -0
  273. claude_mpm/services/core/interfaces/communication.py +343 -0
  274. claude_mpm/services/core/interfaces/infrastructure.py +413 -0
  275. claude_mpm/services/core/interfaces/service.py +434 -0
  276. claude_mpm/services/core/interfaces.py +19 -944
  277. claude_mpm/services/event_aggregator.py +208 -170
  278. claude_mpm/services/exceptions.py +387 -308
  279. claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
  280. claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
  281. claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
  282. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
  283. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
  284. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
  285. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
  286. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
  287. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  288. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  289. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  290. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  291. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
  292. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  293. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  294. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
  295. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
  296. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  297. claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
  298. claude_mpm/services/hook_service.py +106 -114
  299. claude_mpm/services/infrastructure/__init__.py +7 -5
  300. claude_mpm/services/infrastructure/context_preservation.py +233 -199
  301. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  302. claude_mpm/services/infrastructure/logging.py +83 -76
  303. claude_mpm/services/infrastructure/monitoring.py +547 -404
  304. claude_mpm/services/mcp_gateway/__init__.py +30 -13
  305. claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
  306. claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
  307. claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
  308. claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
  309. claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
  310. claude_mpm/services/mcp_gateway/core/base.py +80 -67
  311. claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
  312. claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
  313. claude_mpm/services/mcp_gateway/main.py +287 -137
  314. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  315. claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
  316. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  317. claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
  318. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
  319. claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
  320. claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
  321. claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
  322. claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
  323. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
  324. claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
  325. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
  326. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
  327. claude_mpm/services/memory/__init__.py +2 -2
  328. claude_mpm/services/memory/builder.py +451 -362
  329. claude_mpm/services/memory/cache/__init__.py +2 -2
  330. claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
  331. claude_mpm/services/memory/cache/simple_cache.py +107 -93
  332. claude_mpm/services/memory/indexed_memory.py +195 -193
  333. claude_mpm/services/memory/optimizer.py +267 -234
  334. claude_mpm/services/memory/router.py +571 -263
  335. claude_mpm/services/memory_hook_service.py +237 -0
  336. claude_mpm/services/port_manager.py +575 -0
  337. claude_mpm/services/project/__init__.py +3 -3
  338. claude_mpm/services/project/analyzer.py +451 -305
  339. claude_mpm/services/project/registry.py +262 -240
  340. claude_mpm/services/recovery_manager.py +287 -231
  341. claude_mpm/services/response_tracker.py +87 -67
  342. claude_mpm/services/runner_configuration_service.py +587 -0
  343. claude_mpm/services/session_management_service.py +304 -0
  344. claude_mpm/services/socketio/__init__.py +4 -4
  345. claude_mpm/services/socketio/client_proxy.py +174 -0
  346. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  347. claude_mpm/services/socketio/handlers/base.py +44 -30
  348. claude_mpm/services/socketio/handlers/connection.py +166 -64
  349. claude_mpm/services/socketio/handlers/file.py +123 -108
  350. claude_mpm/services/socketio/handlers/git.py +607 -373
  351. claude_mpm/services/socketio/handlers/hook.py +185 -0
  352. claude_mpm/services/socketio/handlers/memory.py +4 -4
  353. claude_mpm/services/socketio/handlers/project.py +4 -4
  354. claude_mpm/services/socketio/handlers/registry.py +53 -38
  355. claude_mpm/services/socketio/server/__init__.py +18 -0
  356. claude_mpm/services/socketio/server/broadcaster.py +252 -0
  357. claude_mpm/services/socketio/server/core.py +399 -0
  358. claude_mpm/services/socketio/server/main.py +323 -0
  359. claude_mpm/services/socketio_client_manager.py +160 -133
  360. claude_mpm/services/socketio_server.py +36 -1885
  361. claude_mpm/services/subprocess_launcher_service.py +316 -0
  362. claude_mpm/services/system_instructions_service.py +258 -0
  363. claude_mpm/services/ticket_manager.py +19 -533
  364. claude_mpm/services/utility_service.py +285 -0
  365. claude_mpm/services/version_control/__init__.py +18 -21
  366. claude_mpm/services/version_control/branch_strategy.py +20 -10
  367. claude_mpm/services/version_control/conflict_resolution.py +37 -13
  368. claude_mpm/services/version_control/git_operations.py +52 -21
  369. claude_mpm/services/version_control/semantic_versioning.py +92 -53
  370. claude_mpm/services/version_control/version_parser.py +145 -125
  371. claude_mpm/services/version_service.py +270 -0
  372. claude_mpm/storage/__init__.py +2 -2
  373. claude_mpm/storage/state_storage.py +177 -181
  374. claude_mpm/ticket_wrapper.py +2 -2
  375. claude_mpm/utils/__init__.py +2 -2
  376. claude_mpm/utils/agent_dependency_loader.py +453 -243
  377. claude_mpm/utils/config_manager.py +157 -118
  378. claude_mpm/utils/console.py +1 -1
  379. claude_mpm/utils/dependency_cache.py +102 -107
  380. claude_mpm/utils/dependency_manager.py +52 -47
  381. claude_mpm/utils/dependency_strategies.py +131 -96
  382. claude_mpm/utils/environment_context.py +110 -102
  383. claude_mpm/utils/error_handler.py +75 -55
  384. claude_mpm/utils/file_utils.py +80 -67
  385. claude_mpm/utils/framework_detection.py +12 -11
  386. claude_mpm/utils/import_migration_example.py +12 -60
  387. claude_mpm/utils/imports.py +48 -45
  388. claude_mpm/utils/path_operations.py +100 -93
  389. claude_mpm/utils/robust_installer.py +172 -164
  390. claude_mpm/utils/session_logging.py +30 -23
  391. claude_mpm/utils/subprocess_utils.py +99 -61
  392. claude_mpm/validation/__init__.py +1 -1
  393. claude_mpm/validation/agent_validator.py +151 -111
  394. claude_mpm/validation/frontmatter_validator.py +92 -71
  395. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/METADATA +90 -22
  396. claude_mpm-4.0.4.dist-info/RECORD +417 -0
  397. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/entry_points.txt +1 -0
  398. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/licenses/LICENSE +1 -1
  399. claude_mpm/cli/commands/run_guarded.py +0 -511
  400. claude_mpm/config/memory_guardian_config.py +0 -325
  401. claude_mpm/config/memory_guardian_yaml.py +0 -335
  402. claude_mpm/core/config_paths.py +0 -150
  403. claude_mpm/core/memory_aware_runner.py +0 -353
  404. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  405. claude_mpm/deployment_paths.py +0 -261
  406. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  407. claude_mpm/models/state_models.py +0 -433
  408. claude_mpm/services/agent/__init__.py +0 -24
  409. claude_mpm/services/agent/deployment.py +0 -2548
  410. claude_mpm/services/agent/management.py +0 -598
  411. claude_mpm/services/agent/registry.py +0 -813
  412. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  413. claude_mpm/services/communication/socketio.py +0 -1935
  414. claude_mpm/services/communication/websocket.py +0 -479
  415. claude_mpm/services/framework_claude_md_generator.py +0 -624
  416. claude_mpm/services/health_monitor.py +0 -893
  417. claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
  418. claude_mpm/services/infrastructure/health_monitor.py +0 -775
  419. claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
  420. claude_mpm/services/infrastructure/memory_guardian.py +0 -944
  421. claude_mpm/services/infrastructure/restart_protection.py +0 -642
  422. claude_mpm/services/infrastructure/state_manager.py +0 -774
  423. claude_mpm/services/mcp_gateway/manager.py +0 -334
  424. claude_mpm/services/optimized_hook_service.py +0 -542
  425. claude_mpm/services/project_analyzer.py +0 -864
  426. claude_mpm/services/project_registry.py +0 -608
  427. claude_mpm/services/standalone_socketio_server.py +0 -1300
  428. claude_mpm/services/ticket_manager_di.py +0 -318
  429. claude_mpm/services/ticketing_service_original.py +0 -510
  430. claude_mpm/utils/paths.py +0 -395
  431. claude_mpm/utils/platform_memory.py +0 -524
  432. claude_mpm-3.9.11.dist-info/RECORD +0 -306
  433. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/WHEEL +0 -0
  434. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,5 @@
1
+ from pathlib import Path
2
+
1
3
  """Context Preservation service for handling Claude conversation data.
2
4
 
3
5
  This service specializes in parsing and preserving Claude's conversation
@@ -10,86 +12,115 @@ Design Principles:
10
12
  - Graceful handling of corrupted data
11
13
  """
12
14
 
13
- import json
14
- import logging
15
15
  import gzip
16
+ import json
16
17
  import os
17
18
  import shutil
18
19
  import tempfile
19
20
  from datetime import datetime, timedelta
20
- from pathlib import Path
21
- from typing import Dict, Any, List, Optional, Iterator, Tuple
21
+ from typing import Any, Dict, Iterator, List, Optional, Tuple
22
+
22
23
  import ijson # For streaming JSON parsing
24
+ from dataclasses import dataclass, field
23
25
 
24
26
  from claude_mpm.services.core.base import BaseService
25
- from claude_mpm.models.state_models import ConversationState, ConversationContext
27
+
28
+
29
+ @dataclass
30
+ class ConversationContext:
31
+ """Context information for a single conversation."""
32
+
33
+ conversation_id: str
34
+ title: str
35
+ message_count: int
36
+ last_message_time: float
37
+ file_references: List[str] = field(default_factory=list)
38
+ open_tabs: List[str] = field(default_factory=list)
39
+ tags: List[str] = field(default_factory=list)
40
+ is_active: bool = False
41
+
42
+
43
+ @dataclass
44
+ class ConversationState:
45
+ """Claude conversation state and context."""
46
+
47
+ active_conversation_id: Optional[str]
48
+ active_conversation: Optional[ConversationContext]
49
+ recent_conversations: List[ConversationContext]
50
+ total_conversations: int
51
+ total_storage_mb: float
52
+ preferences: Dict[str, Any]
53
+ open_files: List[str]
54
+ recent_files: List[str]
55
+ pinned_files: List[str]
26
56
 
27
57
 
28
58
  class ContextPreservationService(BaseService):
29
59
  """Service for preserving and managing Claude conversation context."""
30
-
60
+
31
61
  def __init__(self, claude_dir: Optional[Path] = None):
32
62
  """Initialize Context Preservation service.
33
-
63
+
34
64
  Args:
35
65
  claude_dir: Claude configuration directory (default: ~/.claude)
36
66
  """
37
67
  super().__init__("ContextPreservation")
38
-
68
+
39
69
  # Claude configuration paths
40
70
  self.claude_dir = claude_dir or Path.home() / ".claude"
41
71
  self.claude_json_path = self.claude_dir / ".claude.json"
42
72
  self.claude_backup_dir = self.claude_dir / "backups"
43
-
73
+
44
74
  # Size thresholds
45
75
  self.large_file_threshold_mb = 100
46
76
  self.compression_threshold_mb = 50
47
-
77
+
48
78
  # Context extraction limits
49
79
  self.max_conversations_to_extract = 10
50
80
  self.max_messages_per_conversation = 100
51
81
  self.max_file_references = 1000
52
-
82
+
53
83
  # Statistics
54
84
  self.files_processed = 0
55
85
  self.total_size_processed_mb = 0.0
56
-
86
+
57
87
  self.log_info(f"Context Preservation initialized for: {self.claude_dir}")
58
-
88
+
59
89
  async def initialize(self) -> bool:
60
90
  """Initialize the Context Preservation service.
61
-
91
+
62
92
  Returns:
63
93
  True if initialization successful
64
94
  """
65
95
  try:
66
96
  self.log_info("Initializing Context Preservation service")
67
-
97
+
68
98
  # Ensure backup directory exists
69
99
  self.claude_backup_dir.mkdir(parents=True, exist_ok=True)
70
-
100
+
71
101
  # Check Claude configuration
72
102
  if self.claude_json_path.exists():
73
103
  size_mb = self.claude_json_path.stat().st_size / (1024 * 1024)
74
104
  self.log_info(f"Found Claude configuration: {size_mb:.2f}MB")
75
105
  else:
76
106
  self.log_warning("Claude configuration not found")
77
-
107
+
78
108
  self._initialized = True
79
109
  self.log_info("Context Preservation service initialized successfully")
80
110
  return True
81
-
111
+
82
112
  except Exception as e:
83
113
  self.log_error(f"Failed to initialize Context Preservation: {e}")
84
114
  return False
85
-
86
- async def parse_claude_json(self,
87
- extract_full: bool = False) -> Optional[ConversationState]:
115
+
116
+ async def parse_claude_json(
117
+ self, extract_full: bool = False
118
+ ) -> Optional[ConversationState]:
88
119
  """Parse Claude's .claude.json file safely.
89
-
120
+
90
121
  Args:
91
122
  extract_full: Whether to extract full conversation data
92
-
123
+
93
124
  Returns:
94
125
  ConversationState object or None if parsing failed
95
126
  """
@@ -97,223 +128,229 @@ class ContextPreservationService(BaseService):
97
128
  if not self.claude_json_path.exists():
98
129
  self.log_debug("Claude configuration file not found")
99
130
  return self._empty_conversation_state()
100
-
131
+
101
132
  file_size_mb = self.claude_json_path.stat().st_size / (1024 * 1024)
102
133
  self.log_info(f"Parsing Claude configuration: {file_size_mb:.2f}MB")
103
-
134
+
104
135
  # Choose parsing strategy based on file size
105
136
  if file_size_mb > self.large_file_threshold_mb:
106
137
  self.log_info("Using streaming parser for large file")
107
138
  return await self._parse_large_claude_json(extract_full)
108
139
  else:
109
140
  return await self._parse_standard_claude_json(extract_full)
110
-
141
+
111
142
  except Exception as e:
112
143
  self.log_error(f"Failed to parse Claude JSON: {e}")
113
144
  return None
114
-
145
+
115
146
  async def extract_active_conversation(self) -> Optional[ConversationContext]:
116
147
  """Extract only the active conversation context.
117
-
148
+
118
149
  Returns:
119
150
  ConversationContext for active conversation or None
120
151
  """
121
152
  try:
122
153
  if not self.claude_json_path.exists():
123
154
  return None
124
-
155
+
125
156
  # Use streaming to find active conversation
126
- with open(self.claude_json_path, 'rb') as f:
157
+ with open(self.claude_json_path, "rb") as f:
127
158
  parser = ijson.parse(f)
128
-
159
+
129
160
  active_conv_id = None
130
161
  in_conversations = False
131
162
  current_conv = {}
132
-
163
+
133
164
  for prefix, event, value in parser:
134
165
  # Get active conversation ID
135
- if prefix == 'activeConversationId':
166
+ if prefix == "activeConversationId":
136
167
  active_conv_id = value
137
-
168
+
138
169
  # Parse conversations array
139
- elif prefix.startswith('conversations.item'):
140
- if event == 'map_key':
170
+ elif prefix.startswith("conversations.item"):
171
+ if event == "map_key":
141
172
  current_key = value
142
- elif active_conv_id and current_conv.get('id') == active_conv_id:
173
+ elif (
174
+ active_conv_id and current_conv.get("id") == active_conv_id
175
+ ):
143
176
  # Found active conversation
144
177
  return self._create_conversation_context(current_conv)
145
-
178
+
146
179
  return None
147
-
180
+
148
181
  except Exception as e:
149
182
  self.log_error(f"Failed to extract active conversation: {e}")
150
183
  return None
151
-
152
- async def compress_conversation_history(self,
153
- keep_recent_days: int = 7) -> bool:
184
+
185
+ async def compress_conversation_history(self, keep_recent_days: int = 7) -> bool:
154
186
  """Compress large conversation histories.
155
-
187
+
156
188
  Args:
157
189
  keep_recent_days: Days of recent conversations to keep uncompressed
158
-
190
+
159
191
  Returns:
160
192
  True if compression successful
161
193
  """
162
194
  try:
163
195
  if not self.claude_json_path.exists():
164
196
  return False
165
-
197
+
166
198
  file_size_mb = self.claude_json_path.stat().st_size / (1024 * 1024)
167
-
199
+
168
200
  if file_size_mb < self.compression_threshold_mb:
169
201
  self.log_debug(f"File too small for compression: {file_size_mb:.2f}MB")
170
202
  return False
171
-
203
+
172
204
  self.log_info(f"Compressing conversation history: {file_size_mb:.2f}MB")
173
-
205
+
174
206
  # Create backup first
175
207
  backup_path = await self._create_backup()
176
-
208
+
177
209
  # Load and filter conversations
178
210
  cutoff_time = datetime.now().timestamp() - (keep_recent_days * 86400)
179
-
180
- with open(self.claude_json_path, 'r') as f:
211
+
212
+ with open(self.claude_json_path, "r") as f:
181
213
  data = json.load(f)
182
-
183
- original_count = len(data.get('conversations', []))
184
-
214
+
215
+ original_count = len(data.get("conversations", []))
216
+
185
217
  # Filter conversations
186
218
  recent_conversations = []
187
219
  archived_conversations = []
188
-
189
- for conv in data.get('conversations', []):
190
- updated_at = conv.get('updatedAt', 0) / 1000 # Convert from ms
220
+
221
+ for conv in data.get("conversations", []):
222
+ updated_at = conv.get("updatedAt", 0) / 1000 # Convert from ms
191
223
  if updated_at >= cutoff_time:
192
224
  recent_conversations.append(conv)
193
225
  else:
194
226
  # Create minimal version for archive
195
- archived_conversations.append({
196
- 'id': conv.get('id'),
197
- 'title': conv.get('title'),
198
- 'createdAt': conv.get('createdAt'),
199
- 'updatedAt': conv.get('updatedAt'),
200
- 'messageCount': len(conv.get('messages', [])),
201
- 'archived': True
202
- })
203
-
227
+ archived_conversations.append(
228
+ {
229
+ "id": conv.get("id"),
230
+ "title": conv.get("title"),
231
+ "createdAt": conv.get("createdAt"),
232
+ "updatedAt": conv.get("updatedAt"),
233
+ "messageCount": len(conv.get("messages", [])),
234
+ "archived": True,
235
+ }
236
+ )
237
+
204
238
  # Update data with filtered conversations
205
- data['conversations'] = recent_conversations
206
- data['archivedConversations'] = archived_conversations
207
-
239
+ data["conversations"] = recent_conversations
240
+ data["archivedConversations"] = archived_conversations
241
+
208
242
  # Write compressed version
209
- temp_path = self.claude_json_path.with_suffix('.tmp')
210
- with open(temp_path, 'w') as f:
211
- json.dump(data, f, separators=(',', ':')) # Compact format
212
-
243
+ temp_path = self.claude_json_path.with_suffix(".tmp")
244
+ with open(temp_path, "w") as f:
245
+ json.dump(data, f, separators=(",", ":")) # Compact format
246
+
213
247
  # Replace original
214
248
  temp_path.replace(self.claude_json_path)
215
-
249
+
216
250
  new_size_mb = self.claude_json_path.stat().st_size / (1024 * 1024)
217
251
  reduction_pct = ((file_size_mb - new_size_mb) / file_size_mb) * 100
218
-
219
- self.log_info(f"Compression complete: {original_count} -> {len(recent_conversations)} "
220
- f"conversations, {file_size_mb:.2f}MB -> {new_size_mb:.2f}MB "
221
- f"({reduction_pct:.1f}% reduction)")
222
-
252
+
253
+ self.log_info(
254
+ f"Compression complete: {original_count} -> {len(recent_conversations)} "
255
+ f"conversations, {file_size_mb:.2f}MB -> {new_size_mb:.2f}MB "
256
+ f"({reduction_pct:.1f}% reduction)"
257
+ )
258
+
223
259
  return True
224
-
260
+
225
261
  except Exception as e:
226
262
  self.log_error(f"Failed to compress conversation history: {e}")
227
263
  return False
228
-
229
- async def handle_file_references(self,
230
- conversation: Dict[str, Any]) -> List[str]:
264
+
265
+ async def handle_file_references(self, conversation: Dict[str, Any]) -> List[str]:
231
266
  """Extract and validate file references from conversation.
232
-
267
+
233
268
  Args:
234
269
  conversation: Conversation data dictionary
235
-
270
+
236
271
  Returns:
237
272
  List of valid file paths referenced in conversation
238
273
  """
239
274
  try:
240
275
  files = set()
241
-
276
+
242
277
  # Extract from messages
243
- for message in conversation.get('messages', [])[:self.max_messages_per_conversation]:
278
+ for message in conversation.get("messages", [])[
279
+ : self.max_messages_per_conversation
280
+ ]:
244
281
  # Extract from content
245
- content = message.get('content', '')
282
+ content = message.get("content", "")
246
283
  if isinstance(content, str):
247
284
  files.update(self._extract_file_paths(content))
248
-
285
+
249
286
  # Extract from attachments
250
- for attachment in message.get('attachments', []):
251
- if attachment.get('type') == 'file':
252
- file_path = attachment.get('path')
287
+ for attachment in message.get("attachments", []):
288
+ if attachment.get("type") == "file":
289
+ file_path = attachment.get("path")
253
290
  if file_path:
254
291
  files.add(file_path)
255
-
292
+
256
293
  # Validate file existence
257
294
  valid_files = []
258
- for file_path in list(files)[:self.max_file_references]:
295
+ for file_path in list(files)[: self.max_file_references]:
259
296
  try:
260
297
  if Path(file_path).exists():
261
298
  valid_files.append(file_path)
262
299
  except:
263
300
  pass # Invalid path
264
-
301
+
265
302
  return valid_files
266
-
303
+
267
304
  except Exception as e:
268
305
  self.log_error(f"Failed to handle file references: {e}")
269
306
  return []
270
-
307
+
271
308
  async def preserve_user_preferences(self) -> Dict[str, Any]:
272
309
  """Extract and preserve user preferences and settings.
273
-
310
+
274
311
  Returns:
275
312
  Dictionary of user preferences
276
313
  """
277
314
  try:
278
315
  if not self.claude_json_path.exists():
279
316
  return {}
280
-
317
+
281
318
  # Use streaming to extract preferences
282
319
  preferences = {}
283
-
284
- with open(self.claude_json_path, 'rb') as f:
320
+
321
+ with open(self.claude_json_path, "rb") as f:
285
322
  parser = ijson.parse(f)
286
-
323
+
287
324
  for prefix, event, value in parser:
288
- if prefix.startswith('preferences'):
325
+ if prefix.startswith("preferences"):
289
326
  # Extract preference key and value
290
- if event == 'map_key':
327
+ if event == "map_key":
291
328
  current_key = value
292
- elif event in ('string', 'number', 'boolean'):
329
+ elif event in ("string", "number", "boolean"):
293
330
  preferences[current_key] = value
294
-
331
+
295
332
  self.log_debug(f"Preserved {len(preferences)} user preferences")
296
333
  return preferences
297
-
334
+
298
335
  except Exception as e:
299
336
  self.log_error(f"Failed to preserve user preferences: {e}")
300
337
  return {}
301
-
302
- async def _parse_standard_claude_json(self,
303
- extract_full: bool) -> ConversationState:
338
+
339
+ async def _parse_standard_claude_json(
340
+ self, extract_full: bool
341
+ ) -> ConversationState:
304
342
  """Parse Claude JSON using standard JSON parser."""
305
343
  try:
306
- with open(self.claude_json_path, 'r') as f:
344
+ with open(self.claude_json_path, "r") as f:
307
345
  data = json.load(f)
308
-
346
+
309
347
  return await self._extract_conversation_state(data, extract_full)
310
-
348
+
311
349
  except json.JSONDecodeError as e:
312
350
  self.log_error(f"JSON decode error: {e}")
313
351
  return self._empty_conversation_state()
314
-
315
- async def _parse_large_claude_json(self,
316
- extract_full: bool) -> ConversationState:
352
+
353
+ async def _parse_large_claude_json(self, extract_full: bool) -> ConversationState:
317
354
  """Parse large Claude JSON using streaming parser."""
318
355
  try:
319
356
  # Extract key data using streaming
@@ -321,56 +358,56 @@ class ContextPreservationService(BaseService):
321
358
  conversations = []
322
359
  preferences = {}
323
360
  open_files = []
324
-
325
- with open(self.claude_json_path, 'rb') as f:
361
+
362
+ with open(self.claude_json_path, "rb") as f:
326
363
  parser = ijson.parse(f)
327
-
364
+
328
365
  conversation_count = 0
329
366
  current_conv = {}
330
367
  in_conversation = False
331
-
368
+
332
369
  for prefix, event, value in parser:
333
370
  # Limit conversations extracted
334
371
  if conversation_count >= self.max_conversations_to_extract:
335
372
  break
336
-
373
+
337
374
  # Extract active conversation ID
338
- if prefix == 'activeConversationId':
375
+ if prefix == "activeConversationId":
339
376
  active_conv_id = value
340
-
377
+
341
378
  # Extract conversations
342
- elif prefix.startswith('conversations.item'):
343
- if event == 'start_map':
379
+ elif prefix.startswith("conversations.item"):
380
+ if event == "start_map":
344
381
  in_conversation = True
345
382
  current_conv = {}
346
- elif event == 'end_map':
383
+ elif event == "end_map":
347
384
  if in_conversation and current_conv:
348
385
  conversations.append(current_conv)
349
386
  conversation_count += 1
350
387
  in_conversation = False
351
388
  current_conv = {}
352
- elif in_conversation and event == 'map_key':
389
+ elif in_conversation and event == "map_key":
353
390
  current_key = value
354
391
  elif in_conversation and current_key:
355
392
  current_conv[current_key] = value
356
-
393
+
357
394
  # Extract open files
358
- elif prefix.startswith('openFiles.item'):
359
- if event == 'string':
395
+ elif prefix.startswith("openFiles.item"):
396
+ if event == "string":
360
397
  open_files.append(value)
361
-
398
+
362
399
  # Find active conversation
363
400
  active_conv = None
364
401
  recent_convs = []
365
-
402
+
366
403
  for conv in conversations:
367
- if conv.get('id') == active_conv_id:
404
+ if conv.get("id") == active_conv_id:
368
405
  active_conv = self._create_conversation_context(conv)
369
406
  else:
370
407
  recent_convs.append(self._create_conversation_context(conv))
371
-
408
+
372
409
  file_size_mb = self.claude_json_path.stat().st_size / (1024 * 1024)
373
-
410
+
374
411
  return ConversationState(
375
412
  active_conversation_id=active_conv_id,
376
413
  active_conversation=active_conv,
@@ -380,77 +417,74 @@ class ContextPreservationService(BaseService):
380
417
  preferences=preferences,
381
418
  open_files=open_files[:100],
382
419
  recent_files=[],
383
- pinned_files=[]
420
+ pinned_files=[],
384
421
  )
385
-
422
+
386
423
  except Exception as e:
387
424
  self.log_error(f"Failed to parse large Claude JSON: {e}")
388
425
  return self._empty_conversation_state()
389
-
390
- async def _extract_conversation_state(self,
391
- data: Dict[str, Any],
392
- extract_full: bool) -> ConversationState:
426
+
427
+ async def _extract_conversation_state(
428
+ self, data: Dict[str, Any], extract_full: bool
429
+ ) -> ConversationState:
393
430
  """Extract conversation state from parsed data."""
394
431
  try:
395
- conversations = data.get('conversations', [])
396
- active_conv_id = data.get('activeConversationId')
397
-
432
+ conversations = data.get("conversations", [])
433
+ active_conv_id = data.get("activeConversationId")
434
+
398
435
  # Find active conversation
399
436
  active_conv = None
400
437
  if active_conv_id:
401
438
  for conv in conversations:
402
- if conv.get('id') == active_conv_id:
439
+ if conv.get("id") == active_conv_id:
403
440
  active_conv = self._create_conversation_context(conv)
404
441
  break
405
-
442
+
406
443
  # Get recent conversations
407
444
  recent_convs = []
408
445
  if extract_full:
409
446
  sorted_convs = sorted(
410
- conversations,
411
- key=lambda c: c.get('updatedAt', 0),
412
- reverse=True
413
- )[:self.max_conversations_to_extract]
414
-
447
+ conversations, key=lambda c: c.get("updatedAt", 0), reverse=True
448
+ )[: self.max_conversations_to_extract]
449
+
415
450
  for conv in sorted_convs:
416
- if conv.get('id') != active_conv_id:
451
+ if conv.get("id") != active_conv_id:
417
452
  recent_convs.append(self._create_conversation_context(conv))
418
-
453
+
419
454
  file_size_mb = self.claude_json_path.stat().st_size / (1024 * 1024)
420
-
455
+
421
456
  return ConversationState(
422
457
  active_conversation_id=active_conv_id,
423
458
  active_conversation=active_conv,
424
459
  recent_conversations=recent_convs,
425
460
  total_conversations=len(conversations),
426
461
  total_storage_mb=file_size_mb,
427
- preferences=data.get('preferences', {}),
428
- open_files=data.get('openFiles', [])[:100],
429
- recent_files=data.get('recentFiles', [])[:100],
430
- pinned_files=data.get('pinnedFiles', [])[:50]
462
+ preferences=data.get("preferences", {}),
463
+ open_files=data.get("openFiles", [])[:100],
464
+ recent_files=data.get("recentFiles", [])[:100],
465
+ pinned_files=data.get("pinnedFiles", [])[:50],
431
466
  )
432
-
467
+
433
468
  except Exception as e:
434
469
  self.log_error(f"Failed to extract conversation state: {e}")
435
470
  return self._empty_conversation_state()
436
-
437
- def _create_conversation_context(self,
438
- conv: Dict[str, Any]) -> ConversationContext:
471
+
472
+ def _create_conversation_context(self, conv: Dict[str, Any]) -> ConversationContext:
439
473
  """Create ConversationContext from conversation data."""
440
474
  return ConversationContext(
441
- conversation_id=conv.get('id', ''),
442
- title=conv.get('title', 'Untitled'),
443
- created_at=conv.get('createdAt', 0) / 1000, # Convert from ms
444
- updated_at=conv.get('updatedAt', 0) / 1000,
445
- message_count=len(conv.get('messages', [])),
446
- total_tokens=conv.get('totalTokens', 0),
447
- max_tokens=conv.get('maxTokens', 100000),
475
+ conversation_id=conv.get("id", ""),
476
+ title=conv.get("title", "Untitled"),
477
+ created_at=conv.get("createdAt", 0) / 1000, # Convert from ms
478
+ updated_at=conv.get("updatedAt", 0) / 1000,
479
+ message_count=len(conv.get("messages", [])),
480
+ total_tokens=conv.get("totalTokens", 0),
481
+ max_tokens=conv.get("maxTokens", 100000),
448
482
  referenced_files=[], # Would need full extraction
449
- open_tabs=conv.get('openTabs', []),
450
- tags=conv.get('tags', []),
451
- is_active=False
483
+ open_tabs=conv.get("openTabs", []),
484
+ tags=conv.get("tags", []),
485
+ is_active=False,
452
486
  )
453
-
487
+
454
488
  def _empty_conversation_state(self) -> ConversationState:
455
489
  """Create empty conversation state."""
456
490
  return ConversationState(
@@ -462,76 +496,76 @@ class ContextPreservationService(BaseService):
462
496
  preferences={},
463
497
  open_files=[],
464
498
  recent_files=[],
465
- pinned_files=[]
499
+ pinned_files=[],
466
500
  )
467
-
501
+
468
502
  def _extract_file_paths(self, content: str) -> List[str]:
469
503
  """Extract file paths from message content."""
470
504
  import re
471
-
505
+
472
506
  files = set()
473
-
507
+
474
508
  # Common file path patterns
475
509
  patterns = [
476
510
  r'[\'"`]([/\\]?(?:[a-zA-Z]:)?[/\\]?[\w\-_./\\]+\.\w+)[\'"`]',
477
- r'(?:^|\s)([/\\]?(?:[a-zA-Z]:)?[/\\]?[\w\-_./\\]+\.\w+)(?:\s|$)',
511
+ r"(?:^|\s)([/\\]?(?:[a-zA-Z]:)?[/\\]?[\w\-_./\\]+\.\w+)(?:\s|$)",
478
512
  ]
479
-
513
+
480
514
  for pattern in patterns:
481
515
  matches = re.findall(pattern, content)
482
516
  files.update(matches)
483
-
517
+
484
518
  return list(files)
485
-
519
+
486
520
  async def _create_backup(self) -> Path:
487
521
  """Create backup of Claude configuration."""
488
522
  try:
489
523
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
490
524
  backup_name = f"claude_backup_{timestamp}.json"
491
-
525
+
492
526
  # Compress if large
493
527
  file_size_mb = self.claude_json_path.stat().st_size / (1024 * 1024)
494
528
  if file_size_mb > self.compression_threshold_mb:
495
529
  backup_name += ".gz"
496
530
  backup_path = self.claude_backup_dir / backup_name
497
-
498
- with open(self.claude_json_path, 'rb') as f_in:
499
- with gzip.open(backup_path, 'wb') as f_out:
531
+
532
+ with open(self.claude_json_path, "rb") as f_in:
533
+ with gzip.open(backup_path, "wb") as f_out:
500
534
  shutil.copyfileobj(f_in, f_out)
501
535
  else:
502
536
  backup_path = self.claude_backup_dir / backup_name
503
537
  shutil.copy2(self.claude_json_path, backup_path)
504
-
538
+
505
539
  self.log_info(f"Created backup: {backup_path}")
506
540
  return backup_path
507
-
541
+
508
542
  except Exception as e:
509
543
  self.log_error(f"Failed to create backup: {e}")
510
544
  raise
511
-
545
+
512
546
  def get_statistics(self) -> Dict[str, Any]:
513
547
  """Get context preservation statistics.
514
-
548
+
515
549
  Returns:
516
550
  Dictionary containing statistics
517
551
  """
518
552
  claude_size_mb = 0.0
519
553
  if self.claude_json_path.exists():
520
554
  claude_size_mb = self.claude_json_path.stat().st_size / (1024 * 1024)
521
-
555
+
522
556
  backup_count = 0
523
557
  backup_size_mb = 0.0
524
558
  if self.claude_backup_dir.exists():
525
559
  backups = list(self.claude_backup_dir.glob("claude_backup_*.json*"))
526
560
  backup_count = len(backups)
527
561
  backup_size_mb = sum(f.stat().st_size for f in backups) / (1024 * 1024)
528
-
562
+
529
563
  return {
530
- 'claude_config_exists': self.claude_json_path.exists(),
531
- 'claude_config_size_mb': round(claude_size_mb, 2),
532
- 'is_large_file': claude_size_mb > self.large_file_threshold_mb,
533
- 'backup_count': backup_count,
534
- 'total_backup_size_mb': round(backup_size_mb, 2),
535
- 'files_processed': self.files_processed,
536
- 'total_size_processed_mb': round(self.total_size_processed_mb, 2)
537
- }
564
+ "claude_config_exists": self.claude_json_path.exists(),
565
+ "claude_config_size_mb": round(claude_size_mb, 2),
566
+ "is_large_file": claude_size_mb > self.large_file_threshold_mb,
567
+ "backup_count": backup_count,
568
+ "total_backup_size_mb": round(backup_size_mb, 2),
569
+ "files_processed": self.files_processed,
570
+ "total_size_processed_mb": round(self.total_size_processed_mb, 2),
571
+ }