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