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
@@ -18,10 +18,11 @@ Key Features:
18
18
  import json
19
19
  import logging
20
20
  import re
21
- import yaml
22
- from pathlib import Path
23
- from typing import Dict, Any, List, Tuple, Optional
24
21
  from dataclasses import dataclass
22
+ from pathlib import Path
23
+ from typing import Any, Dict, List, Optional, Tuple
24
+
25
+ import yaml
25
26
 
26
27
  logger = logging.getLogger(__name__)
27
28
 
@@ -29,18 +30,21 @@ logger = logging.getLogger(__name__)
29
30
  @dataclass
30
31
  class ValidationResult:
31
32
  """Result of frontmatter validation."""
33
+
32
34
  is_valid: bool
33
35
  errors: List[str]
34
36
  warnings: List[str]
35
37
  corrections: List[str]
36
38
  corrected_frontmatter: Optional[Dict[str, Any]] = None
37
- field_corrections: Optional[Dict[str, Any]] = None # Specific field-level corrections
39
+ field_corrections: Optional[
40
+ Dict[str, Any]
41
+ ] = None # Specific field-level corrections
38
42
 
39
43
 
40
44
  class FrontmatterValidator:
41
45
  """
42
46
  Validates and corrects frontmatter in agent files.
43
-
47
+
44
48
  This class handles:
45
49
  - Schema validation against frontmatter_schema.json
46
50
  - Automatic correction of common issues
@@ -48,7 +52,7 @@ class FrontmatterValidator:
48
52
  - Tools field parsing and correction
49
53
  - Logging of all corrections made
50
54
  """
51
-
55
+
52
56
  # Model name mappings for normalization
53
57
  MODEL_MAPPINGS = {
54
58
  # Sonnet variations
@@ -62,7 +66,6 @@ class FrontmatterValidator:
62
66
  "3.5-sonnet": "sonnet",
63
67
  "sonnet-3.5": "sonnet",
64
68
  "sonnet-4": "sonnet",
65
-
66
69
  # Opus variations
67
70
  "claude-3-opus-20240229": "opus",
68
71
  "claude-opus-4-20250514": "opus",
@@ -70,7 +73,6 @@ class FrontmatterValidator:
70
73
  "3-opus": "opus",
71
74
  "opus-3": "opus",
72
75
  "opus-4": "opus",
73
-
74
76
  # Haiku variations
75
77
  "claude-3-haiku-20240307": "haiku",
76
78
  "claude-3-5-haiku-20241022": "haiku",
@@ -78,7 +80,7 @@ class FrontmatterValidator:
78
80
  "haiku-3": "haiku",
79
81
  "haiku-3.5": "haiku",
80
82
  }
81
-
83
+
82
84
  # Tool name corrections (case normalization)
83
85
  TOOL_CORRECTIONS = {
84
86
  "read": "Read",
@@ -96,51 +98,83 @@ class FrontmatterValidator:
96
98
  "todowrite": "TodoWrite",
97
99
  "exitplanmode": "ExitPlanMode",
98
100
  }
99
-
101
+
100
102
  # Valid tool names
101
103
  VALID_TOOLS = {
102
- "Read", "Write", "Edit", "MultiEdit", "Grep", "Glob", "LS", "Bash",
103
- "WebSearch", "WebFetch", "NotebookRead", "NotebookEdit", "TodoWrite",
104
- "ExitPlanMode", "git", "docker", "kubectl", "terraform", "aws", "gcloud", "azure"
104
+ "Read",
105
+ "Write",
106
+ "Edit",
107
+ "MultiEdit",
108
+ "Grep",
109
+ "Glob",
110
+ "LS",
111
+ "Bash",
112
+ "WebSearch",
113
+ "WebFetch",
114
+ "NotebookRead",
115
+ "NotebookEdit",
116
+ "TodoWrite",
117
+ "ExitPlanMode",
118
+ "git",
119
+ "docker",
120
+ "kubectl",
121
+ "terraform",
122
+ "aws",
123
+ "gcloud",
124
+ "azure",
105
125
  }
106
-
126
+
107
127
  # Valid model tiers
108
128
  VALID_MODELS = {"opus", "sonnet", "haiku"}
109
-
129
+
110
130
  def __init__(self):
111
131
  """Initialize the validator with schema if available."""
112
132
  self.schema = self._load_schema()
113
133
  self.all_valid_fields = self._extract_valid_fields()
114
-
134
+
115
135
  def _load_schema(self) -> Optional[Dict[str, Any]]:
116
136
  """Load the frontmatter schema from JSON file."""
117
- schema_path = Path(__file__).parent.parent / "schemas" / "frontmatter_schema.json"
137
+ schema_path = (
138
+ Path(__file__).parent.parent / "schemas" / "frontmatter_schema.json"
139
+ )
118
140
  if schema_path.exists():
119
141
  try:
120
- with open(schema_path, 'r') as f:
142
+ with open(schema_path, "r") as f:
121
143
  return json.load(f)
122
144
  except Exception as e:
123
145
  logger.warning(f"Failed to load frontmatter schema: {e}")
124
146
  return None
125
-
147
+
126
148
  def _extract_valid_fields(self) -> set:
127
149
  """Extract all valid field names from the schema."""
128
- if self.schema and 'properties' in self.schema:
129
- return set(self.schema['properties'].keys())
150
+ if self.schema and "properties" in self.schema:
151
+ return set(self.schema["properties"].keys())
130
152
  # Fallback to known fields if schema not available
131
153
  return {
132
- "name", "description", "version", "base_version", "author",
133
- "tools", "model", "tags", "category", "max_tokens", "temperature",
134
- "resource_tier", "dependencies", "capabilities", "color"
154
+ "name",
155
+ "description",
156
+ "version",
157
+ "base_version",
158
+ "author",
159
+ "tools",
160
+ "model",
161
+ "tags",
162
+ "category",
163
+ "max_tokens",
164
+ "temperature",
165
+ "resource_tier",
166
+ "dependencies",
167
+ "capabilities",
168
+ "color",
135
169
  }
136
-
170
+
137
171
  def validate_and_correct(self, frontmatter: Dict[str, Any]) -> ValidationResult:
138
172
  """
139
173
  Validate and automatically correct frontmatter.
140
-
174
+
141
175
  Args:
142
176
  frontmatter: Dictionary of frontmatter fields
143
-
177
+
144
178
  Returns:
145
179
  ValidationResult with validation status and corrected frontmatter
146
180
  """
@@ -149,18 +183,24 @@ class FrontmatterValidator:
149
183
  corrections = []
150
184
  corrected = frontmatter.copy()
151
185
  field_corrections = {} # Track only the fields that actually need correction
152
-
186
+
153
187
  # Required fields check (from schema)
154
- required_fields = self.schema.get('required', ["name", "description", "version", "model"]) if self.schema else ["name", "description", "version", "model"]
188
+ required_fields = (
189
+ self.schema.get("required", ["name", "description", "version", "model"])
190
+ if self.schema
191
+ else ["name", "description", "version", "model"]
192
+ )
155
193
  for field in required_fields:
156
194
  if field not in corrected:
157
195
  errors.append(f"Missing required field: {field}")
158
-
196
+
159
197
  # Validate and correct name field
160
198
  if "name" in corrected:
161
199
  name = corrected["name"]
162
200
  if not isinstance(name, str):
163
- errors.append(f"Field 'name' must be a string, got {type(name).__name__}")
201
+ errors.append(
202
+ f"Field 'name' must be a string, got {type(name).__name__}"
203
+ )
164
204
  elif not re.match(r"^[a-z][a-z0-9_]*$", name):
165
205
  # Try to fix the name
166
206
  fixed_name = name.lower().replace("-", "_").replace(" ", "_")
@@ -168,33 +208,41 @@ class FrontmatterValidator:
168
208
  if fixed_name and fixed_name[0].isalpha():
169
209
  corrected["name"] = fixed_name
170
210
  field_corrections["name"] = fixed_name
171
- corrections.append(f"Corrected name from '{name}' to '{fixed_name}'")
211
+ corrections.append(
212
+ f"Corrected name from '{name}' to '{fixed_name}'"
213
+ )
172
214
  else:
173
215
  errors.append(f"Invalid name format: {name}")
174
-
216
+
175
217
  # Validate and correct model field
176
218
  if "model" in corrected:
177
219
  model = corrected["model"]
178
-
220
+
179
221
  # Convert to string if it's a number (YAML might parse dates as integers)
180
222
  if isinstance(model, (int, float)):
181
223
  model = str(model)
182
224
  corrected["model"] = model
183
225
  field_corrections["model"] = model
184
226
  corrections.append(f"Converted model from number to string: {model}")
185
-
227
+
186
228
  if not isinstance(model, str):
187
- errors.append(f"Field 'model' must be a string, got {type(model).__name__}")
229
+ errors.append(
230
+ f"Field 'model' must be a string, got {type(model).__name__}"
231
+ )
188
232
  else:
189
233
  normalized_model = self._normalize_model(model)
190
234
  if normalized_model != model:
191
235
  corrected["model"] = normalized_model
192
236
  field_corrections["model"] = normalized_model
193
- corrections.append(f"Normalized model from '{model}' to '{normalized_model}'")
194
-
237
+ corrections.append(
238
+ f"Normalized model from '{model}' to '{normalized_model}'"
239
+ )
240
+
195
241
  if normalized_model not in self.VALID_MODELS:
196
- errors.append(f"Invalid model: {model} (normalized to {normalized_model})")
197
-
242
+ errors.append(
243
+ f"Invalid model: {model} (normalized to {normalized_model})"
244
+ )
245
+
198
246
  # Validate and correct tools field
199
247
  if "tools" in corrected:
200
248
  tools = corrected["tools"]
@@ -203,7 +251,7 @@ class FrontmatterValidator:
203
251
  corrected["tools"] = corrected_tools
204
252
  field_corrections["tools"] = corrected_tools
205
253
  corrections.extend(tool_corrections)
206
-
254
+
207
255
  # Validate tool names
208
256
  invalid_tools = []
209
257
  for tool in corrected_tools:
@@ -215,71 +263,97 @@ class FrontmatterValidator:
215
263
  corrected_tools[idx] = corrected_tool
216
264
  corrected["tools"] = corrected_tools
217
265
  field_corrections["tools"] = corrected_tools
218
- corrections.append(f"Corrected tool '{tool}' to '{corrected_tool}'")
266
+ corrections.append(
267
+ f"Corrected tool '{tool}' to '{corrected_tool}'"
268
+ )
219
269
  else:
220
270
  invalid_tools.append(tool)
221
-
271
+
222
272
  if invalid_tools:
223
273
  warnings.append(f"Unknown tools: {', '.join(invalid_tools)}")
224
-
274
+
225
275
  # Validate version fields
226
276
  version_fields = ["version", "base_version"]
227
277
  for field in version_fields:
228
278
  if field in corrected:
229
279
  version = corrected[field]
230
280
  if not isinstance(version, str):
231
- errors.append(f"Field '{field}' must be a string, got {type(version).__name__}")
281
+ errors.append(
282
+ f"Field '{field}' must be a string, got {type(version).__name__}"
283
+ )
232
284
  elif not re.match(r"^\d+\.\d+\.\d+$", version):
233
285
  # Try to fix common version issues
234
286
  if re.match(r"^\d+\.\d+$", version):
235
287
  fixed_version = f"{version}.0"
236
288
  corrected[field] = fixed_version
237
289
  field_corrections[field] = fixed_version
238
- corrections.append(f"Fixed {field} from '{version}' to '{fixed_version}'")
290
+ corrections.append(
291
+ f"Fixed {field} from '{version}' to '{fixed_version}'"
292
+ )
239
293
  elif re.match(r"^v?\d+\.\d+\.\d+$", version):
240
294
  fixed_version = version.lstrip("v")
241
295
  corrected[field] = fixed_version
242
296
  field_corrections[field] = fixed_version
243
- corrections.append(f"Fixed {field} from '{version}' to '{fixed_version}'")
297
+ corrections.append(
298
+ f"Fixed {field} from '{version}' to '{fixed_version}'"
299
+ )
244
300
  else:
245
301
  errors.append(f"Invalid {field} format: {version}")
246
-
302
+
247
303
  # Validate description
248
304
  if "description" in corrected:
249
305
  desc = corrected["description"]
250
306
  if not isinstance(desc, str):
251
- errors.append(f"Field 'description' must be a string, got {type(desc).__name__}")
307
+ errors.append(
308
+ f"Field 'description' must be a string, got {type(desc).__name__}"
309
+ )
252
310
  elif len(desc) < 10:
253
- warnings.append(f"Description too short ({len(desc)} chars, minimum 10)")
311
+ warnings.append(
312
+ f"Description too short ({len(desc)} chars, minimum 10)"
313
+ )
254
314
  elif len(desc) > 200:
255
- warnings.append(f"Description too long ({len(desc)} chars, maximum 200)")
256
-
315
+ warnings.append(
316
+ f"Description too long ({len(desc)} chars, maximum 200)"
317
+ )
318
+
257
319
  # Validate optional fields
258
320
  if "category" in corrected:
259
- valid_categories = ["engineering", "research", "quality", "operations", "specialized"]
321
+ valid_categories = [
322
+ "engineering",
323
+ "research",
324
+ "quality",
325
+ "operations",
326
+ "specialized",
327
+ ]
260
328
  if corrected["category"] not in valid_categories:
261
329
  warnings.append(f"Invalid category: {corrected['category']}")
262
-
330
+
263
331
  if "resource_tier" in corrected:
264
332
  valid_tiers = ["basic", "standard", "intensive", "lightweight"]
265
333
  if corrected["resource_tier"] not in valid_tiers:
266
334
  warnings.append(f"Invalid resource_tier: {corrected['resource_tier']}")
267
-
335
+
268
336
  # Validate color field
269
337
  if "color" in corrected:
270
338
  color = corrected["color"]
271
339
  if not isinstance(color, str):
272
- errors.append(f"Field 'color' must be a string, got {type(color).__name__}")
340
+ errors.append(
341
+ f"Field 'color' must be a string, got {type(color).__name__}"
342
+ )
273
343
  # Color validation could be expanded to check for valid color names/hex codes
274
-
344
+
275
345
  # Validate author field
276
346
  if "author" in corrected:
277
347
  author = corrected["author"]
278
348
  if not isinstance(author, str):
279
- errors.append(f"Field 'author' must be a string, got {type(author).__name__}")
349
+ errors.append(
350
+ f"Field 'author' must be a string, got {type(author).__name__}"
351
+ )
280
352
  elif len(author) > 100:
281
- warnings.append(f"Author field too long ({len(author)} chars, maximum 100)")
282
-
353
+ warnings.append(
354
+ f"Author field too long ({len(author)} chars, maximum 100)"
355
+ )
356
+
283
357
  # Validate tags field
284
358
  if "tags" in corrected:
285
359
  tags = corrected["tags"]
@@ -288,136 +362,157 @@ class FrontmatterValidator:
288
362
  else:
289
363
  for tag in tags:
290
364
  if not isinstance(tag, str):
291
- errors.append(f"All tags must be strings, found {type(tag).__name__}")
365
+ errors.append(
366
+ f"All tags must be strings, found {type(tag).__name__}"
367
+ )
292
368
  elif not re.match(r"^[a-z][a-z0-9-]*$", tag):
293
- warnings.append(f"Tag '{tag}' doesn't match recommended pattern (lowercase, alphanumeric with hyphens)")
294
-
369
+ warnings.append(
370
+ f"Tag '{tag}' doesn't match recommended pattern (lowercase, alphanumeric with hyphens)"
371
+ )
372
+
295
373
  # Validate numeric fields
296
- for field_name, (min_val, max_val) in [("max_tokens", (1000, 200000)), ("temperature", (0, 1))]:
374
+ for field_name, (min_val, max_val) in [
375
+ ("max_tokens", (1000, 200000)),
376
+ ("temperature", (0, 1)),
377
+ ]:
297
378
  if field_name in corrected:
298
379
  value = corrected[field_name]
299
380
  if field_name == "temperature" and not isinstance(value, (int, float)):
300
- errors.append(f"Field '{field_name}' must be a number, got {type(value).__name__}")
381
+ errors.append(
382
+ f"Field '{field_name}' must be a number, got {type(value).__name__}"
383
+ )
301
384
  elif field_name == "max_tokens" and not isinstance(value, int):
302
- errors.append(f"Field '{field_name}' must be an integer, got {type(value).__name__}")
303
- elif isinstance(value, (int, float)) and not (min_val <= value <= max_val):
304
- warnings.append(f"Field '{field_name}' value {value} outside recommended range [{min_val}, {max_val}]")
305
-
385
+ errors.append(
386
+ f"Field '{field_name}' must be an integer, got {type(value).__name__}"
387
+ )
388
+ elif isinstance(value, (int, float)) and not (
389
+ min_val <= value <= max_val
390
+ ):
391
+ warnings.append(
392
+ f"Field '{field_name}' value {value} outside recommended range [{min_val}, {max_val}]"
393
+ )
394
+
306
395
  # Determine if valid
307
396
  is_valid = len(errors) == 0
308
-
397
+
309
398
  return ValidationResult(
310
399
  is_valid=is_valid,
311
400
  errors=errors,
312
401
  warnings=warnings,
313
402
  corrections=corrections,
314
403
  corrected_frontmatter=corrected if corrections else None,
315
- field_corrections=field_corrections if field_corrections else None
404
+ field_corrections=field_corrections if field_corrections else None,
316
405
  )
317
-
406
+
318
407
  def _normalize_model(self, model: str) -> str:
319
408
  """
320
409
  Normalize model name to standard tier (opus, sonnet, haiku).
321
-
410
+
322
411
  Args:
323
412
  model: Original model name
324
-
413
+
325
414
  Returns:
326
415
  Normalized model tier name
327
416
  """
328
417
  # Direct mapping check
329
418
  if model in self.MODEL_MAPPINGS:
330
419
  return self.MODEL_MAPPINGS[model]
331
-
420
+
332
421
  # Already normalized
333
422
  if model in self.VALID_MODELS:
334
423
  return model
335
-
424
+
336
425
  # Try case-insensitive match
337
426
  model_lower = model.lower()
338
427
  if model_lower in self.VALID_MODELS:
339
428
  return model_lower
340
-
429
+
341
430
  # Check if model contains tier name
342
431
  for tier in self.VALID_MODELS:
343
432
  if tier in model_lower:
344
433
  return tier
345
-
434
+
346
435
  # Default to sonnet if unrecognized
347
436
  logger.warning(f"Unrecognized model '{model}', defaulting to 'sonnet'")
348
437
  return "sonnet"
349
-
438
+
350
439
  def _correct_tools(self, tools: Any) -> Tuple[List[str], List[str]]:
351
440
  """
352
441
  Correct tools field formatting issues.
353
-
442
+
354
443
  Args:
355
444
  tools: Original tools value (could be string, list, etc.)
356
-
445
+
357
446
  Returns:
358
447
  Tuple of (corrected_tools_list, list_of_corrections)
359
448
  """
360
449
  corrections = []
361
-
450
+
362
451
  # If already a proper list, just validate
363
452
  if isinstance(tools, list):
364
453
  return tools, corrections
365
-
454
+
366
455
  # If it's a string, try to parse it
367
456
  if isinstance(tools, str):
368
457
  # Remove any surrounding whitespace
369
458
  tools_str = tools.strip()
370
-
459
+
371
460
  # Check if it's a string representation of a list
372
461
  if tools_str.startswith("[") and tools_str.endswith("]"):
373
462
  # Try to parse as JSON array
374
463
  try:
375
464
  parsed_tools = json.loads(tools_str)
376
465
  if isinstance(parsed_tools, list):
377
- corrections.append(f"Parsed tools from JSON string: {tools_str[:50]}...")
466
+ corrections.append(
467
+ f"Parsed tools from JSON string: {tools_str[:50]}..."
468
+ )
378
469
  return parsed_tools, corrections
379
470
  except json.JSONDecodeError:
380
471
  pass
381
-
472
+
382
473
  # Try to extract comma-separated values
383
474
  tools_str = tools_str[1:-1] # Remove brackets
384
-
475
+
385
476
  # Split by comma and clean up
386
477
  if "," in tools_str:
387
478
  tool_list = [t.strip().strip("'\"") for t in tools_str.split(",")]
388
479
  else:
389
480
  # Single tool or space-separated
390
481
  tool_list = tools_str.replace(",", " ").split()
391
-
482
+
392
483
  # Clean up tool names
393
484
  cleaned_tools = []
394
485
  for tool in tool_list:
395
486
  tool = tool.strip().strip("'\"")
396
487
  if tool:
397
488
  cleaned_tools.append(tool)
398
-
489
+
399
490
  if cleaned_tools:
400
- corrections.append(f"Converted tools from string to list: {len(cleaned_tools)} tools")
491
+ corrections.append(
492
+ f"Converted tools from string to list: {len(cleaned_tools)} tools"
493
+ )
401
494
  return cleaned_tools, corrections
402
-
495
+
403
496
  # If we can't parse it, return empty list
404
- corrections.append(f"Could not parse tools field (type: {type(tools).__name__}), using empty list")
497
+ corrections.append(
498
+ f"Could not parse tools field (type: {type(tools).__name__}), using empty list"
499
+ )
405
500
  return [], corrections
406
-
501
+
407
502
  def validate_file(self, file_path: Path) -> ValidationResult:
408
503
  """
409
504
  Validate frontmatter in a specific file.
410
-
505
+
411
506
  Args:
412
507
  file_path: Path to agent file
413
-
508
+
414
509
  Returns:
415
510
  ValidationResult with validation status
416
511
  """
417
512
  try:
418
- with open(file_path, 'r') as f:
513
+ with open(file_path, "r") as f:
419
514
  content = f.read()
420
-
515
+
421
516
  # Extract frontmatter
422
517
  frontmatter = self._extract_frontmatter(content)
423
518
  if not frontmatter:
@@ -425,27 +520,27 @@ class FrontmatterValidator:
425
520
  is_valid=False,
426
521
  errors=["No frontmatter found in file"],
427
522
  warnings=[],
428
- corrections=[]
523
+ corrections=[],
429
524
  )
430
-
525
+
431
526
  # Validate and correct
432
527
  return self.validate_and_correct(frontmatter)
433
-
528
+
434
529
  except Exception as e:
435
530
  return ValidationResult(
436
531
  is_valid=False,
437
532
  errors=[f"Error reading file: {e}"],
438
533
  warnings=[],
439
- corrections=[]
534
+ corrections=[],
440
535
  )
441
-
536
+
442
537
  def _extract_frontmatter(self, content: str) -> Optional[Dict[str, Any]]:
443
538
  """
444
539
  Extract frontmatter from file content.
445
-
540
+
446
541
  Args:
447
542
  content: File content
448
-
543
+
449
544
  Returns:
450
545
  Parsed frontmatter dictionary or None
451
546
  """
@@ -455,78 +550,80 @@ class FrontmatterValidator:
455
550
  end_marker = content.find("\n---\n", 4)
456
551
  if end_marker == -1:
457
552
  end_marker = content.find("\n---\r\n", 4)
458
-
553
+
459
554
  if end_marker != -1:
460
555
  frontmatter_str = content[4:end_marker]
461
556
  return yaml.safe_load(frontmatter_str)
462
557
  except yaml.YAMLError as e:
463
558
  logger.error(f"Failed to parse YAML frontmatter: {e}")
464
-
559
+
465
560
  return None
466
-
561
+
467
562
  def correct_file(self, file_path: Path, dry_run: bool = False) -> ValidationResult:
468
563
  """
469
564
  Validate and optionally correct a file's frontmatter.
470
-
565
+
471
566
  Args:
472
567
  file_path: Path to agent file
473
568
  dry_run: If True, don't write changes to file
474
-
569
+
475
570
  Returns:
476
571
  ValidationResult with corrections made
477
572
  """
478
573
  result = self.validate_file(file_path)
479
-
574
+
480
575
  if result.field_corrections and not dry_run:
481
576
  try:
482
- with open(file_path, 'r') as f:
577
+ with open(file_path, "r") as f:
483
578
  content = f.read()
484
-
579
+
485
580
  # Find frontmatter boundaries
486
581
  if content.startswith("---"):
487
582
  end_marker = content.find("\n---\n", 4)
488
583
  if end_marker == -1:
489
584
  end_marker = content.find("\n---\r\n", 4)
490
-
585
+
491
586
  if end_marker != -1:
492
587
  # Apply field-level corrections to preserve structure
493
588
  frontmatter_content = content[4:end_marker]
494
589
  corrected_content = self._apply_field_corrections(
495
590
  frontmatter_content, result.field_corrections
496
591
  )
497
-
592
+
498
593
  if corrected_content != frontmatter_content:
499
594
  new_content = f"---\n{corrected_content}\n---\n{content[end_marker + 5:]}"
500
-
501
- with open(file_path, 'w') as f:
595
+
596
+ with open(file_path, "w") as f:
502
597
  f.write(new_content)
503
-
598
+
504
599
  logger.info(f"Corrected frontmatter in {file_path}")
505
600
  for correction in result.corrections:
506
601
  logger.info(f" - {correction}")
507
602
  except Exception as e:
508
603
  logger.error(f"Failed to write corrections to {file_path}: {e}")
509
-
604
+
510
605
  return result
511
-
512
- def _apply_field_corrections(self, frontmatter_content: str, field_corrections: Dict[str, Any]) -> str:
606
+
607
+ def _apply_field_corrections(
608
+ self, frontmatter_content: str, field_corrections: Dict[str, Any]
609
+ ) -> str:
513
610
  """
514
611
  Apply field-level corrections while preserving structure and other fields.
515
-
612
+
516
613
  Args:
517
614
  frontmatter_content: Original YAML frontmatter content
518
615
  field_corrections: Dict of field corrections to apply
519
-
616
+
520
617
  Returns:
521
618
  Corrected frontmatter content
522
619
  """
523
- lines = frontmatter_content.strip().split('\n')
620
+ lines = frontmatter_content.strip().split("\n")
524
621
  corrected_lines = []
525
-
622
+
526
623
  for line in lines:
527
624
  # Check if this line contains a field we need to correct
528
- if ':' in line:
529
- field_name = line.split(':')[0].strip()
625
+ if ":" in line:
626
+ field_name = line.split(":")[0].strip()
530
627
  if field_name in field_corrections:
531
628
  # Replace the field value while preserving structure
532
629
  corrected_value = field_corrections[field_name]
@@ -534,14 +631,16 @@ class FrontmatterValidator:
534
631
  # Handle list fields like tools
535
632
  if field_name == "tools" and isinstance(corrected_value, list):
536
633
  # Format as comma-separated string to preserve existing format
537
- corrected_lines.append(f"{field_name}: {','.join(corrected_value)}")
634
+ corrected_lines.append(
635
+ f"{field_name}: {','.join(corrected_value)}"
636
+ )
538
637
  else:
539
638
  corrected_lines.append(f"{field_name}: {corrected_value}")
540
639
  else:
541
640
  corrected_lines.append(f"{field_name}: {corrected_value}")
542
641
  continue
543
-
642
+
544
643
  # Keep the original line if no correction needed
545
644
  corrected_lines.append(line)
546
-
547
- return '\n'.join(corrected_lines)
645
+
646
+ return "\n".join(corrected_lines)