claude-mpm 3.9.9__py3-none-any.whl → 4.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +2 -2
- claude_mpm/__main__.py +3 -2
- claude_mpm/agents/__init__.py +85 -79
- claude_mpm/agents/agent_loader.py +464 -1003
- claude_mpm/agents/agent_loader_integration.py +45 -45
- claude_mpm/agents/agents_metadata.py +29 -30
- claude_mpm/agents/async_agent_loader.py +156 -138
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/base_agent_loader.py +179 -151
- claude_mpm/agents/frontmatter_validator.py +229 -130
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +213 -147
- claude_mpm/agents/templates/__init__.py +13 -13
- claude_mpm/agents/templates/code_analyzer.json +2 -2
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +23 -11
- claude_mpm/agents/templates/engineer.json +22 -6
- claude_mpm/agents/templates/memory_manager.json +155 -0
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/refactoring_engineer.json +222 -0
- claude_mpm/agents/templates/research.json +20 -14
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/ticketing.json +1 -1
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/agents/templates/web_qa.json +3 -1
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/__init__.py +90 -49
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +21 -18
- claude_mpm/cli/commands/agents.py +279 -247
- claude_mpm/cli/commands/aggregate.py +138 -157
- claude_mpm/cli/commands/cleanup.py +147 -147
- claude_mpm/cli/commands/config.py +93 -76
- claude_mpm/cli/commands/info.py +17 -16
- claude_mpm/cli/commands/mcp.py +143 -762
- claude_mpm/cli/commands/mcp_command_router.py +139 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_install_commands.py +20 -0
- claude_mpm/cli/commands/mcp_server_commands.py +175 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +239 -203
- claude_mpm/cli/commands/monitor.py +203 -81
- claude_mpm/cli/commands/run.py +380 -429
- claude_mpm/cli/commands/run_config_checker.py +160 -0
- claude_mpm/cli/commands/socketio_monitor.py +235 -0
- claude_mpm/cli/commands/tickets.py +305 -197
- claude_mpm/cli/parser.py +24 -1150
- claude_mpm/cli/parsers/__init__.py +29 -0
- claude_mpm/cli/parsers/agents_parser.py +136 -0
- claude_mpm/cli/parsers/base_parser.py +331 -0
- claude_mpm/cli/parsers/config_parser.py +85 -0
- claude_mpm/cli/parsers/mcp_parser.py +152 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +104 -0
- claude_mpm/cli/parsers/run_parser.py +147 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/ticket_cli.py +7 -3
- claude_mpm/cli/utils.py +55 -37
- claude_mpm/cli_module/__init__.py +6 -6
- claude_mpm/cli_module/args.py +188 -140
- claude_mpm/cli_module/commands.py +79 -70
- claude_mpm/cli_module/migration_example.py +38 -60
- claude_mpm/config/__init__.py +32 -25
- claude_mpm/config/agent_config.py +151 -119
- claude_mpm/config/experimental_features.py +217 -0
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +36 -18
- claude_mpm/core/__init__.py +9 -6
- claude_mpm/core/agent_name_normalizer.py +68 -71
- claude_mpm/core/agent_registry.py +372 -521
- claude_mpm/core/agent_session_manager.py +74 -63
- claude_mpm/core/base_service.py +116 -87
- claude_mpm/core/cache.py +119 -153
- claude_mpm/core/claude_runner.py +425 -1120
- claude_mpm/core/config.py +263 -168
- claude_mpm/core/config_aliases.py +69 -61
- claude_mpm/core/config_constants.py +292 -0
- claude_mpm/core/constants.py +57 -99
- claude_mpm/core/container.py +211 -178
- claude_mpm/core/exceptions.py +233 -89
- claude_mpm/core/factories.py +92 -54
- claude_mpm/core/framework_loader.py +378 -220
- claude_mpm/core/hook_manager.py +198 -83
- claude_mpm/core/hook_performance_config.py +136 -0
- claude_mpm/core/injectable_service.py +61 -55
- claude_mpm/core/interactive_session.py +165 -155
- claude_mpm/core/interfaces.py +221 -195
- claude_mpm/core/lazy.py +96 -96
- claude_mpm/core/logger.py +133 -107
- claude_mpm/core/logging_config.py +185 -157
- claude_mpm/core/minimal_framework_loader.py +20 -15
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +215 -181
- claude_mpm/core/optimized_agent_loader.py +134 -138
- claude_mpm/core/optimized_startup.py +159 -157
- claude_mpm/core/pm_hook_interceptor.py +85 -72
- claude_mpm/core/service_registry.py +103 -101
- claude_mpm/core/session_manager.py +97 -87
- claude_mpm/core/socketio_pool.py +212 -158
- claude_mpm/core/tool_access_control.py +58 -51
- claude_mpm/core/types.py +46 -24
- claude_mpm/core/typing_utils.py +166 -82
- claude_mpm/core/unified_agent_registry.py +721 -0
- claude_mpm/core/unified_config.py +550 -0
- claude_mpm/core/unified_paths.py +549 -0
- claude_mpm/dashboard/index.html +1 -1
- claude_mpm/dashboard/open_dashboard.py +51 -17
- claude_mpm/dashboard/static/css/dashboard.css +27 -8
- claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/dist/dashboard.js +2 -0
- claude_mpm/dashboard/static/dist/socket-client.js +2 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
- claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
- claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
- claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
- claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
- claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
- claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
- claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
- claude_mpm/dashboard/static/js/dashboard.js +178 -453
- claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/js/socket-client.js +120 -54
- claude_mpm/dashboard/templates/index.html +40 -50
- claude_mpm/experimental/cli_enhancements.py +60 -58
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +75 -65
- claude_mpm/hooks/__init__.py +1 -1
- claude_mpm/hooks/base_hook.py +33 -28
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
- claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
- claude_mpm/hooks/memory_integration_hook.py +140 -100
- claude_mpm/hooks/tool_call_interceptor.py +89 -76
- claude_mpm/hooks/validation_hooks.py +57 -49
- claude_mpm/init.py +145 -121
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +33 -23
- claude_mpm/models/agent_session.py +228 -200
- claude_mpm/scripts/__init__.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +192 -75
- claude_mpm/scripts/socketio_server_manager.py +328 -0
- claude_mpm/scripts/start_activity_logging.py +25 -22
- claude_mpm/services/__init__.py +68 -43
- claude_mpm/services/agent_capabilities_service.py +271 -0
- claude_mpm/services/agents/__init__.py +23 -32
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
- claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
- claude_mpm/services/agents/deployment/agent_validator.py +352 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
- claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
- claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
- claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
- claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
- claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
- claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
- claude_mpm/services/agents/deployment/results/__init__.py +13 -0
- claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
- claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
- claude_mpm/services/agents/loading/__init__.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
- claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
- claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
- claude_mpm/services/agents/management/__init__.py +2 -2
- claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
- claude_mpm/services/agents/management/agent_management_service.py +209 -156
- claude_mpm/services/agents/memory/__init__.py +9 -6
- claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
- claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
- claude_mpm/services/agents/memory/analyzer.py +430 -0
- claude_mpm/services/agents/memory/content_manager.py +376 -0
- claude_mpm/services/agents/memory/template_generator.py +468 -0
- claude_mpm/services/agents/registry/__init__.py +7 -10
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
- claude_mpm/services/agents/registry/modification_tracker.py +351 -285
- claude_mpm/services/async_session_logger.py +187 -153
- claude_mpm/services/claude_session_logger.py +87 -72
- claude_mpm/services/command_handler_service.py +217 -0
- claude_mpm/services/communication/__init__.py +3 -2
- claude_mpm/services/core/__init__.py +50 -97
- claude_mpm/services/core/base.py +60 -53
- claude_mpm/services/core/interfaces/__init__.py +188 -0
- claude_mpm/services/core/interfaces/agent.py +351 -0
- claude_mpm/services/core/interfaces/communication.py +343 -0
- claude_mpm/services/core/interfaces/infrastructure.py +413 -0
- claude_mpm/services/core/interfaces/service.py +434 -0
- claude_mpm/services/core/interfaces.py +19 -944
- claude_mpm/services/event_aggregator.py +208 -170
- claude_mpm/services/exceptions.py +387 -308
- claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
- claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
- claude_mpm/services/hook_service.py +106 -114
- claude_mpm/services/infrastructure/__init__.py +7 -5
- claude_mpm/services/infrastructure/context_preservation.py +571 -0
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +83 -76
- claude_mpm/services/infrastructure/monitoring.py +547 -404
- claude_mpm/services/mcp_gateway/__init__.py +40 -23
- claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
- claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
- claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
- claude_mpm/services/mcp_gateway/core/__init__.py +14 -21
- claude_mpm/services/mcp_gateway/core/base.py +80 -67
- claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
- claude_mpm/services/mcp_gateway/core/interfaces.py +97 -93
- claude_mpm/services/mcp_gateway/main.py +307 -127
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +100 -101
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +4 -4
- claude_mpm/services/mcp_gateway/server/{mcp_server.py → mcp_gateway.py} +149 -153
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
- claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +110 -121
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
- claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
- claude_mpm/services/memory/__init__.py +2 -2
- claude_mpm/services/memory/builder.py +451 -362
- claude_mpm/services/memory/cache/__init__.py +2 -2
- claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
- claude_mpm/services/memory/cache/simple_cache.py +107 -93
- claude_mpm/services/memory/indexed_memory.py +195 -193
- claude_mpm/services/memory/optimizer.py +267 -234
- claude_mpm/services/memory/router.py +571 -263
- claude_mpm/services/memory_hook_service.py +237 -0
- claude_mpm/services/port_manager.py +223 -0
- claude_mpm/services/project/__init__.py +3 -3
- claude_mpm/services/project/analyzer.py +451 -305
- claude_mpm/services/project/registry.py +262 -240
- claude_mpm/services/recovery_manager.py +287 -231
- claude_mpm/services/response_tracker.py +87 -67
- claude_mpm/services/runner_configuration_service.py +587 -0
- claude_mpm/services/session_management_service.py +304 -0
- claude_mpm/services/socketio/__init__.py +4 -4
- claude_mpm/services/socketio/client_proxy.py +174 -0
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +44 -30
- claude_mpm/services/socketio/handlers/connection.py +145 -65
- claude_mpm/services/socketio/handlers/file.py +123 -108
- claude_mpm/services/socketio/handlers/git.py +607 -373
- claude_mpm/services/socketio/handlers/hook.py +170 -0
- claude_mpm/services/socketio/handlers/memory.py +4 -4
- claude_mpm/services/socketio/handlers/project.py +4 -4
- claude_mpm/services/socketio/handlers/registry.py +53 -38
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +252 -0
- claude_mpm/services/socketio/server/core.py +399 -0
- claude_mpm/services/socketio/server/main.py +323 -0
- claude_mpm/services/socketio_client_manager.py +160 -133
- claude_mpm/services/socketio_server.py +36 -1885
- claude_mpm/services/subprocess_launcher_service.py +316 -0
- claude_mpm/services/system_instructions_service.py +258 -0
- claude_mpm/services/ticket_manager.py +20 -534
- claude_mpm/services/utility_service.py +285 -0
- claude_mpm/services/version_control/__init__.py +18 -21
- claude_mpm/services/version_control/branch_strategy.py +20 -10
- claude_mpm/services/version_control/conflict_resolution.py +37 -13
- claude_mpm/services/version_control/git_operations.py +52 -21
- claude_mpm/services/version_control/semantic_versioning.py +92 -53
- claude_mpm/services/version_control/version_parser.py +145 -125
- claude_mpm/services/version_service.py +270 -0
- claude_mpm/storage/__init__.py +9 -0
- claude_mpm/storage/state_storage.py +552 -0
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/utils/__init__.py +2 -2
- claude_mpm/utils/agent_dependency_loader.py +453 -243
- claude_mpm/utils/config_manager.py +157 -118
- claude_mpm/utils/console.py +1 -1
- claude_mpm/utils/dependency_cache.py +102 -107
- claude_mpm/utils/dependency_manager.py +52 -47
- claude_mpm/utils/dependency_strategies.py +131 -96
- claude_mpm/utils/environment_context.py +110 -102
- claude_mpm/utils/error_handler.py +75 -55
- claude_mpm/utils/file_utils.py +80 -67
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/path_operations.py +100 -93
- claude_mpm/utils/robust_installer.py +172 -164
- claude_mpm/utils/session_logging.py +30 -23
- claude_mpm/utils/subprocess_utils.py +99 -61
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +151 -111
- claude_mpm/validation/frontmatter_validator.py +92 -71
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +51 -2
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
- claude_mpm/models/state_models.py +0 -433
- claude_mpm/services/agent/__init__.py +0 -24
- claude_mpm/services/agent/deployment.py +0 -2548
- claude_mpm/services/agent/management.py +0 -598
- claude_mpm/services/agent/registry.py +0 -813
- claude_mpm/services/agents/registry/agent_registry.py +0 -813
- claude_mpm/services/communication/socketio.py +0 -1935
- claude_mpm/services/communication/websocket.py +0 -479
- claude_mpm/services/framework_claude_md_generator.py +0 -624
- claude_mpm/services/health_monitor.py +0 -893
- claude_mpm/services/infrastructure/memory_guardian.py +0 -770
- claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +0 -444
- claude_mpm/services/optimized_hook_service.py +0 -542
- claude_mpm/services/project_analyzer.py +0 -864
- claude_mpm/services/project_registry.py +0 -608
- claude_mpm/services/standalone_socketio_server.py +0 -1300
- claude_mpm/services/ticket_manager_di.py +0 -318
- claude_mpm/services/ticketing_service_original.py +0 -510
- claude_mpm/utils/paths.py +0 -395
- claude_mpm/utils/platform_memory.py +0 -524
- claude_mpm-3.9.9.dist-info/RECORD +0 -293
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.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[ | 
| 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", | 
| 103 | 
            -
                    " | 
| 104 | 
            -
                    " | 
| 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 =  | 
| 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,  | 
| 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  | 
| 129 | 
            -
                        return set(self.schema[ | 
| 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", | 
| 133 | 
            -
                        " | 
| 134 | 
            -
                        " | 
| 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 =  | 
| 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( | 
| 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( | 
| 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( | 
| 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( | 
| 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( | 
| 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( | 
| 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( | 
| 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( | 
| 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( | 
| 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( | 
| 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( | 
| 311 | 
            +
                            warnings.append(
         | 
| 312 | 
            +
                                f"Description too short ({len(desc)} chars, minimum 10)"
         | 
| 313 | 
            +
                            )
         | 
| 254 314 | 
             
                        elif len(desc) > 200:
         | 
| 255 | 
            -
                            warnings.append( | 
| 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 = [ | 
| 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( | 
| 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( | 
| 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( | 
| 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( | 
| 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( | 
| 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 [ | 
| 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( | 
| 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( | 
| 303 | 
            -
             | 
| 304 | 
            -
                                 | 
| 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( | 
| 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( | 
| 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( | 
| 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,  | 
| 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,  | 
| 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,  | 
| 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( | 
| 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( | 
| 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  | 
| 529 | 
            -
                            field_name = line.split( | 
| 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( | 
| 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  | 
| 645 | 
            +
             | 
| 646 | 
            +
                    return "\n".join(corrected_lines)
         |