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
| @@ -8,18 +8,19 @@ Supports multiple file formats and summarization strategies. | |
| 8 8 | 
             
            Part of ISS-0037: Document Summarizer Tool - Intelligent Document Processing
         | 
| 9 9 | 
             
            """
         | 
| 10 10 |  | 
| 11 | 
            -
            import os
         | 
| 12 | 
            -
            import re
         | 
| 13 | 
            -
            import json
         | 
| 14 | 
            -
            import yaml
         | 
| 15 11 | 
             
            import csv
         | 
| 12 | 
            +
            import hashlib
         | 
| 13 | 
            +
            import json
         | 
| 16 14 | 
             
            import mimetypes
         | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 15 | 
            +
            import os
         | 
| 16 | 
            +
            import re
         | 
| 17 | 
            +
            from collections import OrderedDict
         | 
| 19 18 | 
             
            from datetime import datetime
         | 
| 20 19 | 
             
            from functools import lru_cache
         | 
| 21 | 
            -
            from  | 
| 22 | 
            -
            import  | 
| 20 | 
            +
            from pathlib import Path
         | 
| 21 | 
            +
            from typing import Any, Dict, List, Optional, Tuple
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            import yaml
         | 
| 23 24 |  | 
| 24 25 | 
             
            from claude_mpm.services.mcp_gateway.core.interfaces import (
         | 
| 25 26 | 
             
                MCPToolDefinition,
         | 
| @@ -32,15 +33,15 @@ from claude_mpm.services.mcp_gateway.tools.base_adapter import BaseToolAdapter | |
| 32 33 | 
             
            class LRUCache:
         | 
| 33 34 | 
             
                """
         | 
| 34 35 | 
             
                Simple LRU cache implementation for document summaries.
         | 
| 35 | 
            -
             | 
| 36 | 
            +
             | 
| 36 37 | 
             
                WHY: We need a memory-efficient cache to avoid re-processing documents
         | 
| 37 38 | 
             
                that are accessed repeatedly, which is common in Claude Code sessions.
         | 
| 38 39 | 
             
                """
         | 
| 39 | 
            -
             | 
| 40 | 
            +
             | 
| 40 41 | 
             
                def __init__(self, max_size: int = 100, max_memory_mb: int = 100):
         | 
| 41 42 | 
             
                    """
         | 
| 42 43 | 
             
                    Initialize LRU cache.
         | 
| 43 | 
            -
             | 
| 44 | 
            +
             | 
| 44 45 | 
             
                    Args:
         | 
| 45 46 | 
             
                        max_size: Maximum number of entries
         | 
| 46 47 | 
             
                        max_memory_mb: Maximum memory usage in MB
         | 
| @@ -51,7 +52,7 @@ class LRUCache: | |
| 51 52 | 
             
                    self.current_memory = 0
         | 
| 52 53 | 
             
                    self.hits = 0
         | 
| 53 54 | 
             
                    self.misses = 0
         | 
| 54 | 
            -
             | 
| 55 | 
            +
             | 
| 55 56 | 
             
                def get(self, key: str) -> Optional[Dict[str, Any]]:
         | 
| 56 57 | 
             
                    """Get item from cache, updating LRU order."""
         | 
| 57 58 | 
             
                    if key in self.cache:
         | 
| @@ -61,63 +62,69 @@ class LRUCache: | |
| 61 62 | 
             
                        return self.cache[key]
         | 
| 62 63 | 
             
                    self.misses += 1
         | 
| 63 64 | 
             
                    return None
         | 
| 64 | 
            -
             | 
| 65 | 
            +
             | 
| 65 66 | 
             
                def put(self, key: str, value: Dict[str, Any], size_bytes: int) -> None:
         | 
| 66 67 | 
             
                    """Add item to cache, evicting LRU items if necessary."""
         | 
| 67 68 | 
             
                    # Remove item if it already exists
         | 
| 68 69 | 
             
                    if key in self.cache:
         | 
| 69 | 
            -
                        old_size = self.cache[key].get( | 
| 70 | 
            +
                        old_size = self.cache[key].get("size_bytes", 0)
         | 
| 70 71 | 
             
                        self.current_memory -= old_size
         | 
| 71 72 | 
             
                        del self.cache[key]
         | 
| 72 | 
            -
             | 
| 73 | 
            +
             | 
| 73 74 | 
             
                    # Evict items if necessary
         | 
| 74 | 
            -
                    while ( | 
| 75 | 
            -
             | 
| 75 | 
            +
                    while (
         | 
| 76 | 
            +
                        len(self.cache) >= self.max_size
         | 
| 77 | 
            +
                        or self.current_memory + size_bytes > self.max_memory_bytes
         | 
| 78 | 
            +
                    ):
         | 
| 76 79 | 
             
                        if not self.cache:
         | 
| 77 80 | 
             
                            break
         | 
| 78 81 | 
             
                        # Remove least recently used item
         | 
| 79 82 | 
             
                        removed_key, removed_value = self.cache.popitem(last=False)
         | 
| 80 | 
            -
                        self.current_memory -= removed_value.get( | 
| 81 | 
            -
             | 
| 83 | 
            +
                        self.current_memory -= removed_value.get("size_bytes", 0)
         | 
| 84 | 
            +
             | 
| 82 85 | 
             
                    # Add new item
         | 
| 83 | 
            -
                    value[ | 
| 86 | 
            +
                    value["size_bytes"] = size_bytes
         | 
| 84 87 | 
             
                    self.cache[key] = value
         | 
| 85 88 | 
             
                    self.current_memory += size_bytes
         | 
| 86 | 
            -
             | 
| 89 | 
            +
             | 
| 87 90 | 
             
                def get_stats(self) -> Dict[str, Any]:
         | 
| 88 91 | 
             
                    """Get cache statistics."""
         | 
| 89 | 
            -
                    hit_rate =  | 
| 92 | 
            +
                    hit_rate = (
         | 
| 93 | 
            +
                        self.hits / (self.hits + self.misses)
         | 
| 94 | 
            +
                        if (self.hits + self.misses) > 0
         | 
| 95 | 
            +
                        else 0
         | 
| 96 | 
            +
                    )
         | 
| 90 97 | 
             
                    return {
         | 
| 91 | 
            -
                         | 
| 92 | 
            -
                         | 
| 93 | 
            -
                         | 
| 94 | 
            -
                         | 
| 95 | 
            -
                         | 
| 98 | 
            +
                        "size": len(self.cache),
         | 
| 99 | 
            +
                        "memory_mb": self.current_memory / (1024 * 1024),
         | 
| 100 | 
            +
                        "hits": self.hits,
         | 
| 101 | 
            +
                        "misses": self.misses,
         | 
| 102 | 
            +
                        "hit_rate": hit_rate,
         | 
| 96 103 | 
             
                    }
         | 
| 97 104 |  | 
| 98 105 |  | 
| 99 106 | 
             
            class DocumentSummarizerTool(BaseToolAdapter):
         | 
| 100 107 | 
             
                """
         | 
| 101 108 | 
             
                Document summarizer tool for intelligent document processing.
         | 
| 102 | 
            -
             | 
| 109 | 
            +
             | 
| 103 110 | 
             
                WHY: Claude Code accumulates massive memory from reading full files,
         | 
| 104 111 | 
             
                leading to context overflow. This tool reduces document size by 60%+
         | 
| 105 112 | 
             
                while preserving essential information through intelligent summarization.
         | 
| 106 | 
            -
             | 
| 113 | 
            +
             | 
| 107 114 | 
             
                DESIGN DECISIONS:
         | 
| 108 115 | 
             
                - Use sentence boundary detection to preserve readability
         | 
| 109 116 | 
             
                - Implement multiple summarization modes for different use cases
         | 
| 110 117 | 
             
                - Cache summaries to avoid re-processing frequently accessed files
         | 
| 111 118 | 
             
                - Support common file formats used in development
         | 
| 112 119 | 
             
                """
         | 
| 113 | 
            -
             | 
| 120 | 
            +
             | 
| 114 121 | 
             
                # File size limits (in bytes)
         | 
| 115 122 | 
             
                MAX_FILE_SIZE = 10 * 1024 * 1024  # 10MB
         | 
| 116 123 | 
             
                CHUNK_SIZE = 50000  # Characters per chunk for large files
         | 
| 117 | 
            -
             | 
| 124 | 
            +
             | 
| 118 125 | 
             
                # Token estimation (rough approximation)
         | 
| 119 126 | 
             
                CHARS_PER_TOKEN = 4  # Approximate for Claude's tokenizer
         | 
| 120 | 
            -
             | 
| 127 | 
            +
             | 
| 121 128 | 
             
                def __init__(self):
         | 
| 122 129 | 
             
                    """Initialize the document summarizer tool."""
         | 
| 123 130 | 
             
                    definition = MCPToolDefinition(
         | 
| @@ -128,408 +135,454 @@ class DocumentSummarizerTool(BaseToolAdapter): | |
| 128 135 | 
             
                            "properties": {
         | 
| 129 136 | 
             
                                "file_path": {
         | 
| 130 137 | 
             
                                    "type": "string",
         | 
| 131 | 
            -
                                    "description": "Path to the document file"
         | 
| 138 | 
            +
                                    "description": "Path to the document file",
         | 
| 132 139 | 
             
                                },
         | 
| 133 140 | 
             
                                "mode": {
         | 
| 134 141 | 
             
                                    "type": "string",
         | 
| 135 142 | 
             
                                    "enum": ["brief", "detailed", "key_points", "technical"],
         | 
| 136 143 | 
             
                                    "description": "Summarization mode",
         | 
| 137 | 
            -
                                    "default": "brief"
         | 
| 144 | 
            +
                                    "default": "brief",
         | 
| 138 145 | 
             
                                },
         | 
| 139 146 | 
             
                                "max_tokens": {
         | 
| 140 147 | 
             
                                    "type": "integer",
         | 
| 141 148 | 
             
                                    "description": "Maximum tokens in summary (optional)",
         | 
| 142 149 | 
             
                                    "minimum": 100,
         | 
| 143 | 
            -
                                    "maximum": 10000
         | 
| 150 | 
            +
                                    "maximum": 10000,
         | 
| 144 151 | 
             
                                },
         | 
| 145 152 | 
             
                                "max_percentage": {
         | 
| 146 153 | 
             
                                    "type": "integer",
         | 
| 147 154 | 
             
                                    "description": "Maximum percentage of original to keep (1-100)",
         | 
| 148 155 | 
             
                                    "minimum": 1,
         | 
| 149 156 | 
             
                                    "maximum": 100,
         | 
| 150 | 
            -
                                    "default": 40
         | 
| 157 | 
            +
                                    "default": 40,
         | 
| 151 158 | 
             
                                },
         | 
| 152 159 | 
             
                                "preserve_code": {
         | 
| 153 160 | 
             
                                    "type": "boolean",
         | 
| 154 161 | 
             
                                    "description": "Whether to preserve code blocks intact",
         | 
| 155 | 
            -
                                    "default": True
         | 
| 162 | 
            +
                                    "default": True,
         | 
| 156 163 | 
             
                                },
         | 
| 157 164 | 
             
                                "use_cache": {
         | 
| 158 165 | 
             
                                    "type": "boolean",
         | 
| 159 166 | 
             
                                    "description": "Whether to use cached summaries",
         | 
| 160 | 
            -
                                    "default": True
         | 
| 161 | 
            -
                                }
         | 
| 167 | 
            +
                                    "default": True,
         | 
| 168 | 
            +
                                },
         | 
| 162 169 | 
             
                            },
         | 
| 163 | 
            -
                            "required": ["file_path"]
         | 
| 170 | 
            +
                            "required": ["file_path"],
         | 
| 164 171 | 
             
                        },
         | 
| 165 172 | 
             
                        output_schema={
         | 
| 166 173 | 
             
                            "type": "object",
         | 
| 167 174 | 
             
                            "properties": {
         | 
| 168 175 | 
             
                                "summary": {
         | 
| 169 176 | 
             
                                    "type": "string",
         | 
| 170 | 
            -
                                    "description": "The summarized content"
         | 
| 177 | 
            +
                                    "description": "The summarized content",
         | 
| 171 178 | 
             
                                },
         | 
| 172 179 | 
             
                                "original_size": {
         | 
| 173 180 | 
             
                                    "type": "integer",
         | 
| 174 | 
            -
                                    "description": "Original document size in bytes"
         | 
| 181 | 
            +
                                    "description": "Original document size in bytes",
         | 
| 175 182 | 
             
                                },
         | 
| 176 183 | 
             
                                "summary_size": {
         | 
| 177 184 | 
             
                                    "type": "integer",
         | 
| 178 | 
            -
                                    "description": "Summary size in bytes"
         | 
| 185 | 
            +
                                    "description": "Summary size in bytes",
         | 
| 179 186 | 
             
                                },
         | 
| 180 187 | 
             
                                "reduction_percentage": {
         | 
| 181 188 | 
             
                                    "type": "number",
         | 
| 182 | 
            -
                                    "description": "Percentage reduction achieved"
         | 
| 189 | 
            +
                                    "description": "Percentage reduction achieved",
         | 
| 183 190 | 
             
                                },
         | 
| 184 191 | 
             
                                "token_estimate": {
         | 
| 185 192 | 
             
                                    "type": "object",
         | 
| 186 193 | 
             
                                    "properties": {
         | 
| 187 194 | 
             
                                        "original": {"type": "integer"},
         | 
| 188 195 | 
             
                                        "summary": {"type": "integer"},
         | 
| 189 | 
            -
                                        "saved": {"type": "integer"}
         | 
| 190 | 
            -
                                    }
         | 
| 196 | 
            +
                                        "saved": {"type": "integer"},
         | 
| 197 | 
            +
                                    },
         | 
| 191 198 | 
             
                                },
         | 
| 192 199 | 
             
                                "chunks_processed": {
         | 
| 193 200 | 
             
                                    "type": "integer",
         | 
| 194 | 
            -
                                    "description": "Number of chunks processed for large files"
         | 
| 201 | 
            +
                                    "description": "Number of chunks processed for large files",
         | 
| 195 202 | 
             
                                },
         | 
| 196 203 | 
             
                                "cache_hit": {
         | 
| 197 204 | 
             
                                    "type": "boolean",
         | 
| 198 | 
            -
                                    "description": "Whether summary was retrieved from cache"
         | 
| 199 | 
            -
                                }
         | 
| 200 | 
            -
                            }
         | 
| 205 | 
            +
                                    "description": "Whether summary was retrieved from cache",
         | 
| 206 | 
            +
                                },
         | 
| 207 | 
            +
                            },
         | 
| 201 208 | 
             
                        },
         | 
| 202 209 | 
             
                        version="1.0.0",
         | 
| 203 210 | 
             
                        metadata={
         | 
| 204 211 | 
             
                            "category": "document_processing",
         | 
| 205 | 
            -
                            "supported_formats": [ | 
| 206 | 
            -
             | 
| 212 | 
            +
                            "supported_formats": [
         | 
| 213 | 
            +
                                "txt",
         | 
| 214 | 
            +
                                "md",
         | 
| 215 | 
            +
                                "pdf",
         | 
| 216 | 
            +
                                "docx",
         | 
| 217 | 
            +
                                "json",
         | 
| 218 | 
            +
                                "yaml",
         | 
| 219 | 
            +
                                "csv",
         | 
| 220 | 
            +
                                "py",
         | 
| 221 | 
            +
                                "js",
         | 
| 222 | 
            +
                                "ts",
         | 
| 223 | 
            +
                                "java",
         | 
| 224 | 
            +
                                "cpp",
         | 
| 225 | 
            +
                                "c",
         | 
| 226 | 
            +
                                "h",
         | 
| 227 | 
            +
                                "hpp",
         | 
| 228 | 
            +
                            ],
         | 
| 229 | 
            +
                        },
         | 
| 207 230 | 
             
                    )
         | 
| 208 231 | 
             
                    super().__init__(definition)
         | 
| 209 | 
            -
             | 
| 232 | 
            +
             | 
| 210 233 | 
             
                    # Initialize cache
         | 
| 211 234 | 
             
                    self._cache = LRUCache(max_size=100, max_memory_mb=50)
         | 
| 212 | 
            -
             | 
| 235 | 
            +
             | 
| 213 236 | 
             
                    # Sentence boundary patterns
         | 
| 214 | 
            -
                    self._sentence_endings = re.compile(r | 
| 215 | 
            -
             | 
| 237 | 
            +
                    self._sentence_endings = re.compile(r"[.!?]\s+")
         | 
| 238 | 
            +
             | 
| 216 239 | 
             
                    # Code block patterns for different formats
         | 
| 217 240 | 
             
                    self._code_patterns = {
         | 
| 218 | 
            -
                         | 
| 219 | 
            -
                         | 
| 220 | 
            -
                         | 
| 241 | 
            +
                        "markdown": re.compile(r"```[\s\S]*?```", re.MULTILINE),
         | 
| 242 | 
            +
                        "inline": re.compile(r"`[^`]+`"),
         | 
| 243 | 
            +
                        "indent": re.compile(r"^(    |\t).*$", re.MULTILINE),
         | 
| 221 244 | 
             
                    }
         | 
| 222 | 
            -
             | 
| 245 | 
            +
             | 
| 223 246 | 
             
                def _get_file_hash(self, file_path: str) -> str:
         | 
| 224 247 | 
             
                    """Generate hash for file caching."""
         | 
| 225 248 | 
             
                    stat = os.stat(file_path)
         | 
| 226 249 | 
             
                    hash_input = f"{file_path}:{stat.st_size}:{stat.st_mtime}"
         | 
| 227 250 | 
             
                    return hashlib.md5(hash_input.encode()).hexdigest()
         | 
| 228 | 
            -
             | 
| 251 | 
            +
             | 
| 229 252 | 
             
                def _estimate_tokens(self, text: str) -> int:
         | 
| 230 253 | 
             
                    """Estimate token count for text."""
         | 
| 231 254 | 
             
                    return len(text) // self.CHARS_PER_TOKEN
         | 
| 232 | 
            -
             | 
| 255 | 
            +
             | 
| 233 256 | 
             
                def _validate_file(self, file_path: str) -> Tuple[bool, Optional[str]]:
         | 
| 234 257 | 
             
                    """
         | 
| 235 258 | 
             
                    Validate file for processing.
         | 
| 236 | 
            -
             | 
| 259 | 
            +
             | 
| 237 260 | 
             
                    Returns:
         | 
| 238 261 | 
             
                        Tuple of (is_valid, error_message)
         | 
| 239 262 | 
             
                    """
         | 
| 240 263 | 
             
                    path = Path(file_path)
         | 
| 241 | 
            -
             | 
| 264 | 
            +
             | 
| 242 265 | 
             
                    # Check if file exists
         | 
| 243 266 | 
             
                    if not path.exists():
         | 
| 244 267 | 
             
                        return False, f"File not found: {file_path}"
         | 
| 245 | 
            -
             | 
| 268 | 
            +
             | 
| 246 269 | 
             
                    # Check if it's a file (not directory)
         | 
| 247 270 | 
             
                    if not path.is_file():
         | 
| 248 271 | 
             
                        return False, f"Path is not a file: {file_path}"
         | 
| 249 | 
            -
             | 
| 272 | 
            +
             | 
| 250 273 | 
             
                    # Check file size
         | 
| 251 274 | 
             
                    file_size = path.stat().st_size
         | 
| 252 275 | 
             
                    if file_size > self.MAX_FILE_SIZE:
         | 
| 253 | 
            -
                        return  | 
| 254 | 
            -
             | 
| 276 | 
            +
                        return (
         | 
| 277 | 
            +
                            False,
         | 
| 278 | 
            +
                            f"File too large: {file_size} bytes (max: {self.MAX_FILE_SIZE})",
         | 
| 279 | 
            +
                        )
         | 
| 280 | 
            +
             | 
| 255 281 | 
             
                    # Check file extension
         | 
| 256 | 
            -
                    extension = path.suffix.lower().lstrip( | 
| 257 | 
            -
                    supported = self._definition.metadata.get( | 
| 282 | 
            +
                    extension = path.suffix.lower().lstrip(".")
         | 
| 283 | 
            +
                    supported = self._definition.metadata.get("supported_formats", [])
         | 
| 258 284 | 
             
                    if extension and extension not in supported:
         | 
| 259 285 | 
             
                        # Try to detect by mime type
         | 
| 260 286 | 
             
                        mime_type, _ = mimetypes.guess_type(file_path)
         | 
| 261 | 
            -
                        if not mime_type or not mime_type.startswith( | 
| 287 | 
            +
                        if not mime_type or not mime_type.startswith("text/"):
         | 
| 262 288 | 
             
                            return False, f"Unsupported file format: {extension}"
         | 
| 263 | 
            -
             | 
| 289 | 
            +
             | 
| 264 290 | 
             
                    return True, None
         | 
| 265 | 
            -
             | 
| 291 | 
            +
             | 
| 266 292 | 
             
                def _read_file(self, file_path: str) -> str:
         | 
| 267 293 | 
             
                    """
         | 
| 268 294 | 
             
                    Read file content with appropriate encoding.
         | 
| 269 | 
            -
             | 
| 295 | 
            +
             | 
| 270 296 | 
             
                    Args:
         | 
| 271 297 | 
             
                        file_path: Path to file
         | 
| 272 | 
            -
             | 
| 298 | 
            +
             | 
| 273 299 | 
             
                    Returns:
         | 
| 274 300 | 
             
                        File content as string
         | 
| 275 301 | 
             
                    """
         | 
| 276 302 | 
             
                    path = Path(file_path)
         | 
| 277 | 
            -
             | 
| 303 | 
            +
             | 
| 278 304 | 
             
                    # Try different encodings
         | 
| 279 | 
            -
                    encodings = [ | 
| 280 | 
            -
             | 
| 305 | 
            +
                    encodings = ["utf-8", "latin-1", "cp1252"]
         | 
| 306 | 
            +
             | 
| 281 307 | 
             
                    for encoding in encodings:
         | 
| 282 308 | 
             
                        try:
         | 
| 283 309 | 
             
                            return path.read_text(encoding=encoding)
         | 
| 284 310 | 
             
                        except UnicodeDecodeError:
         | 
| 285 311 | 
             
                            continue
         | 
| 286 | 
            -
             | 
| 312 | 
            +
             | 
| 287 313 | 
             
                    # If all fail, read as binary and decode with errors='ignore'
         | 
| 288 | 
            -
                    with open(file_path,  | 
| 314 | 
            +
                    with open(file_path, "rb") as f:
         | 
| 289 315 | 
             
                        content = f.read()
         | 
| 290 | 
            -
                        return content.decode( | 
| 291 | 
            -
             | 
| 316 | 
            +
                        return content.decode("utf-8", errors="ignore")
         | 
| 317 | 
            +
             | 
| 292 318 | 
             
                def _extract_code_blocks(self, text: str) -> Tuple[List[str], str]:
         | 
| 293 319 | 
             
                    """
         | 
| 294 320 | 
             
                    Extract code blocks from text for preservation.
         | 
| 295 | 
            -
             | 
| 321 | 
            +
             | 
| 296 322 | 
             
                    Returns:
         | 
| 297 323 | 
             
                        Tuple of (code_blocks, text_without_code)
         | 
| 298 324 | 
             
                    """
         | 
| 299 325 | 
             
                    code_blocks = []
         | 
| 300 326 | 
             
                    placeholder_template = "[[CODE_BLOCK_{}]]"
         | 
| 301 | 
            -
             | 
| 327 | 
            +
             | 
| 302 328 | 
             
                    # Extract markdown code blocks
         | 
| 303 | 
            -
                    for match in self._code_patterns[ | 
| 329 | 
            +
                    for match in self._code_patterns["markdown"].finditer(text):
         | 
| 304 330 | 
             
                        code_blocks.append(match.group(0))
         | 
| 305 | 
            -
                        text = text.replace( | 
| 306 | 
            -
             | 
| 331 | 
            +
                        text = text.replace(
         | 
| 332 | 
            +
                            match.group(0), placeholder_template.format(len(code_blocks) - 1)
         | 
| 333 | 
            +
                        )
         | 
| 334 | 
            +
             | 
| 307 335 | 
             
                    return code_blocks, text
         | 
| 308 | 
            -
             | 
| 336 | 
            +
             | 
| 309 337 | 
             
                def _restore_code_blocks(self, text: str, code_blocks: List[str]) -> str:
         | 
| 310 338 | 
             
                    """Restore code blocks to summarized text."""
         | 
| 311 339 | 
             
                    for i, block in enumerate(code_blocks):
         | 
| 312 340 | 
             
                        placeholder = f"[[CODE_BLOCK_{i}]]"
         | 
| 313 341 | 
             
                        text = text.replace(placeholder, block)
         | 
| 314 342 | 
             
                    return text
         | 
| 315 | 
            -
             | 
| 343 | 
            +
             | 
| 316 344 | 
             
                def _truncate_at_sentence(self, text: str, max_chars: int) -> str:
         | 
| 317 345 | 
             
                    """
         | 
| 318 346 | 
             
                    Truncate text at sentence boundary.
         | 
| 319 | 
            -
             | 
| 347 | 
            +
             | 
| 320 348 | 
             
                    WHY: Truncating mid-sentence makes summaries harder to read and
         | 
| 321 349 | 
             
                    can lose important context. Sentence boundaries preserve meaning.
         | 
| 322 350 | 
             
                    """
         | 
| 323 351 | 
             
                    if len(text) <= max_chars:
         | 
| 324 352 | 
             
                        return text
         | 
| 325 | 
            -
             | 
| 353 | 
            +
             | 
| 326 354 | 
             
                    # Find sentence boundaries
         | 
| 327 355 | 
             
                    sentences = self._sentence_endings.split(text)
         | 
| 328 | 
            -
             | 
| 356 | 
            +
             | 
| 329 357 | 
             
                    result = []
         | 
| 330 358 | 
             
                    current_length = 0
         | 
| 331 | 
            -
             | 
| 359 | 
            +
             | 
| 332 360 | 
             
                    for i, sentence in enumerate(sentences):
         | 
| 333 361 | 
             
                        # Add sentence ending back if not last sentence
         | 
| 334 362 | 
             
                        if i < len(sentences) - 1:
         | 
| 335 | 
            -
                            sentence +=  | 
| 336 | 
            -
             | 
| 363 | 
            +
                            sentence += ". "
         | 
| 364 | 
            +
             | 
| 337 365 | 
             
                        if current_length + len(sentence) <= max_chars:
         | 
| 338 366 | 
             
                            result.append(sentence)
         | 
| 339 367 | 
             
                            current_length += len(sentence)
         | 
| 340 368 | 
             
                        else:
         | 
| 341 369 | 
             
                            # Add partial sentence if we haven't added anything yet
         | 
| 342 370 | 
             
                            if not result and sentence:
         | 
| 343 | 
            -
                                result.append(sentence[:max_chars - 3] +  | 
| 371 | 
            +
                                result.append(sentence[: max_chars - 3] + "...")
         | 
| 344 372 | 
             
                            break
         | 
| 345 | 
            -
             | 
| 346 | 
            -
                    return  | 
| 347 | 
            -
             | 
| 373 | 
            +
             | 
| 374 | 
            +
                    return "".join(result)
         | 
| 375 | 
            +
             | 
| 348 376 | 
             
                def _summarize_brief(self, text: str, max_chars: int) -> str:
         | 
| 349 377 | 
             
                    """
         | 
| 350 378 | 
             
                    Brief summarization - first and last portions.
         | 
| 351 | 
            -
             | 
| 379 | 
            +
             | 
| 352 380 | 
             
                    WHY: For quick overview, showing beginning and end gives context
         | 
| 353 381 | 
             
                    about what the document covers and its conclusions.
         | 
| 354 382 | 
             
                    """
         | 
| 355 383 | 
             
                    if len(text) <= max_chars:
         | 
| 356 384 | 
             
                        return text
         | 
| 357 | 
            -
             | 
| 385 | 
            +
             | 
| 358 386 | 
             
                    # Split available space between beginning and end
         | 
| 359 387 | 
             
                    half_chars = max_chars // 2 - 20  # Reserve space for separator
         | 
| 360 | 
            -
             | 
| 388 | 
            +
             | 
| 361 389 | 
             
                    beginning = self._truncate_at_sentence(text, half_chars)
         | 
| 362 | 
            -
                    ending = self._truncate_at_sentence(text[-half_chars * 2:], half_chars)
         | 
| 363 | 
            -
             | 
| 390 | 
            +
                    ending = self._truncate_at_sentence(text[-half_chars * 2 :], half_chars)
         | 
| 391 | 
            +
             | 
| 364 392 | 
             
                    return f"{beginning}\n\n[... content omitted for brevity ...]\n\n{ending}"
         | 
| 365 | 
            -
             | 
| 393 | 
            +
             | 
| 366 394 | 
             
                def _summarize_detailed(self, text: str, max_chars: int) -> str:
         | 
| 367 395 | 
             
                    """
         | 
| 368 396 | 
             
                    Detailed summarization - extract key paragraphs.
         | 
| 369 | 
            -
             | 
| 397 | 
            +
             | 
| 370 398 | 
             
                    WHY: For technical documents, we want to preserve more structure
         | 
| 371 399 | 
             
                    and include middle sections that might contain important details.
         | 
| 372 400 | 
             
                    """
         | 
| 373 401 | 
             
                    if len(text) <= max_chars:
         | 
| 374 402 | 
             
                        return text
         | 
| 375 | 
            -
             | 
| 403 | 
            +
             | 
| 376 404 | 
             
                    # Split into paragraphs
         | 
| 377 | 
            -
                    paragraphs = text.split( | 
| 378 | 
            -
             | 
| 405 | 
            +
                    paragraphs = text.split("\n\n")
         | 
| 406 | 
            +
             | 
| 379 407 | 
             
                    # Calculate importance scores (based on length and position)
         | 
| 380 408 | 
             
                    scored_paragraphs = []
         | 
| 381 409 | 
             
                    for i, para in enumerate(paragraphs):
         | 
| 382 410 | 
             
                        # Skip empty paragraphs
         | 
| 383 411 | 
             
                        if not para.strip():
         | 
| 384 412 | 
             
                            continue
         | 
| 385 | 
            -
             | 
| 413 | 
            +
             | 
| 386 414 | 
             
                        # Score based on position (beginning and end are important)
         | 
| 387 415 | 
             
                        position_score = 1.0
         | 
| 388 416 | 
             
                        if i < 3:  # First 3 paragraphs
         | 
| 389 417 | 
             
                            position_score = 2.0
         | 
| 390 418 | 
             
                        elif i >= len(paragraphs) - 3:  # Last 3 paragraphs
         | 
| 391 419 | 
             
                            position_score = 1.5
         | 
| 392 | 
            -
             | 
| 420 | 
            +
             | 
| 393 421 | 
             
                        # Score based on content indicators
         | 
| 394 422 | 
             
                        content_score = 1.0
         | 
| 395 | 
            -
                        if any( | 
| 423 | 
            +
                        if any(
         | 
| 424 | 
            +
                            keyword in para.lower()
         | 
| 425 | 
            +
                            for keyword in ["summary", "conclusion", "important", "note", "warning"]
         | 
| 426 | 
            +
                        ):
         | 
| 396 427 | 
             
                            content_score = 1.5
         | 
| 397 | 
            -
             | 
| 428 | 
            +
             | 
| 398 429 | 
             
                        score = position_score * content_score * (1 + len(para) / 1000)
         | 
| 399 430 | 
             
                        scored_paragraphs.append((score, i, para))
         | 
| 400 | 
            -
             | 
| 431 | 
            +
             | 
| 401 432 | 
             
                    # Sort by score and select top paragraphs
         | 
| 402 433 | 
             
                    scored_paragraphs.sort(reverse=True)
         | 
| 403 | 
            -
             | 
| 434 | 
            +
             | 
| 404 435 | 
             
                    selected = []
         | 
| 405 436 | 
             
                    current_length = 0
         | 
| 406 | 
            -
             | 
| 437 | 
            +
             | 
| 407 438 | 
             
                    for score, original_index, para in scored_paragraphs:
         | 
| 408 | 
            -
                        truncated_para = self._truncate_at_sentence( | 
| 439 | 
            +
                        truncated_para = self._truncate_at_sentence(
         | 
| 440 | 
            +
                            para, max_chars - current_length
         | 
| 441 | 
            +
                        )
         | 
| 409 442 | 
             
                        if current_length + len(truncated_para) <= max_chars:
         | 
| 410 443 | 
             
                            selected.append((original_index, truncated_para))
         | 
| 411 444 | 
             
                            current_length += len(truncated_para) + 2  # Account for newlines
         | 
| 412 | 
            -
             | 
| 445 | 
            +
             | 
| 413 446 | 
             
                        if current_length >= max_chars * 0.9:  # Stop at 90% to leave some buffer
         | 
| 414 447 | 
             
                            break
         | 
| 415 | 
            -
             | 
| 448 | 
            +
             | 
| 416 449 | 
             
                    # Sort selected paragraphs by original order
         | 
| 417 450 | 
             
                    selected.sort()
         | 
| 418 | 
            -
             | 
| 419 | 
            -
                    return  | 
| 420 | 
            -
             | 
| 451 | 
            +
             | 
| 452 | 
            +
                    return "\n\n".join(para for _, para in selected)
         | 
| 453 | 
            +
             | 
| 421 454 | 
             
                def _summarize_key_points(self, text: str, max_chars: int) -> str:
         | 
| 422 455 | 
             
                    """
         | 
| 423 456 | 
             
                    Extract key points and bullet points.
         | 
| 424 | 
            -
             | 
| 457 | 
            +
             | 
| 425 458 | 
             
                    WHY: Many documents have lists, bullet points, or numbered items
         | 
| 426 459 | 
             
                    that contain the most important information in condensed form.
         | 
| 427 460 | 
             
                    """
         | 
| 428 461 | 
             
                    if len(text) <= max_chars:
         | 
| 429 462 | 
             
                        return text
         | 
| 430 | 
            -
             | 
| 431 | 
            -
                    lines = text.split( | 
| 463 | 
            +
             | 
| 464 | 
            +
                    lines = text.split("\n")
         | 
| 432 465 | 
             
                    key_lines = []
         | 
| 433 | 
            -
             | 
| 466 | 
            +
             | 
| 434 467 | 
             
                    # Patterns for identifying key points
         | 
| 435 468 | 
             
                    list_patterns = [
         | 
| 436 | 
            -
                        re.compile(r | 
| 437 | 
            -
                        re.compile(r | 
| 438 | 
            -
                        re.compile(r | 
| 439 | 
            -
                        re.compile(r | 
| 440 | 
            -
                        re.compile(r | 
| 469 | 
            +
                        re.compile(r"^\s*[-*•]\s+"),  # Bullet points
         | 
| 470 | 
            +
                        re.compile(r"^\s*\d+[.)]\s+"),  # Numbered lists
         | 
| 471 | 
            +
                        re.compile(r"^\s*[A-Z][.)]\s+"),  # Letter lists
         | 
| 472 | 
            +
                        re.compile(r"^#+\s+"),  # Markdown headers
         | 
| 473 | 
            +
                        re.compile(r"^[A-Z][^.!?]*:"),  # Definition lists
         | 
| 441 474 | 
             
                    ]
         | 
| 442 | 
            -
             | 
| 475 | 
            +
             | 
| 443 476 | 
             
                    # Extract lines that match key point patterns
         | 
| 444 477 | 
             
                    for line in lines:
         | 
| 445 478 | 
             
                        if any(pattern.match(line) for pattern in list_patterns):
         | 
| 446 479 | 
             
                            key_lines.append(line)
         | 
| 447 | 
            -
             | 
| 480 | 
            +
             | 
| 448 481 | 
             
                    # If we found key points, use them
         | 
| 449 482 | 
             
                    if key_lines:
         | 
| 450 | 
            -
                        result =  | 
| 483 | 
            +
                        result = "\n".join(key_lines)
         | 
| 451 484 | 
             
                        if len(result) <= max_chars:
         | 
| 452 485 | 
             
                            return result
         | 
| 453 486 | 
             
                        else:
         | 
| 454 487 | 
             
                            return self._truncate_at_sentence(result, max_chars)
         | 
| 455 | 
            -
             | 
| 488 | 
            +
             | 
| 456 489 | 
             
                    # Fallback to brief summary if no key points found
         | 
| 457 490 | 
             
                    return self._summarize_brief(text, max_chars)
         | 
| 458 | 
            -
             | 
| 459 | 
            -
                def _summarize_technical( | 
| 491 | 
            +
             | 
| 492 | 
            +
                def _summarize_technical(
         | 
| 493 | 
            +
                    self, text: str, max_chars: int, preserve_code: bool
         | 
| 494 | 
            +
                ) -> str:
         | 
| 460 495 | 
             
                    """
         | 
| 461 496 | 
             
                    Technical summarization - preserve code and technical details.
         | 
| 462 | 
            -
             | 
| 497 | 
            +
             | 
| 463 498 | 
             
                    WHY: For code files and technical documentation, we need to
         | 
| 464 499 | 
             
                    preserve function signatures, class definitions, and important code.
         | 
| 465 500 | 
             
                    """
         | 
| 466 501 | 
             
                    if len(text) <= max_chars:
         | 
| 467 502 | 
             
                        return text
         | 
| 468 | 
            -
             | 
| 503 | 
            +
             | 
| 469 504 | 
             
                    # Extract and preserve code blocks if requested
         | 
| 470 505 | 
             
                    code_blocks = []
         | 
| 471 506 | 
             
                    text_without_code = text
         | 
| 472 | 
            -
             | 
| 507 | 
            +
             | 
| 473 508 | 
             
                    if preserve_code:
         | 
| 474 509 | 
             
                        code_blocks, text_without_code = self._extract_code_blocks(text)
         | 
| 475 | 
            -
             | 
| 510 | 
            +
             | 
| 476 511 | 
             
                    # Extract technical patterns
         | 
| 477 512 | 
             
                    tech_patterns = [
         | 
| 478 | 
            -
                        re.compile( | 
| 479 | 
            -
             | 
| 480 | 
            -
                         | 
| 481 | 
            -
                        re.compile( | 
| 513 | 
            +
                        re.compile(
         | 
| 514 | 
            +
                            r"^(class|def|function|interface|struct)\s+\w+.*$", re.MULTILINE
         | 
| 515 | 
            +
                        ),  # Definitions
         | 
| 516 | 
            +
                        re.compile(
         | 
| 517 | 
            +
                            r"^(import|from|require|include|using)\s+.*$", re.MULTILINE
         | 
| 518 | 
            +
                        ),  # Imports
         | 
| 519 | 
            +
                        re.compile(r"^\s*@\w+.*$", re.MULTILINE),  # Decorators/Annotations
         | 
| 520 | 
            +
                        re.compile(
         | 
| 521 | 
            +
                            r"^(public|private|protected|static).*\{?$", re.MULTILINE
         | 
| 522 | 
            +
                        ),  # Method signatures
         | 
| 482 523 | 
             
                    ]
         | 
| 483 | 
            -
             | 
| 524 | 
            +
             | 
| 484 525 | 
             
                    important_lines = []
         | 
| 485 526 | 
             
                    for pattern in tech_patterns:
         | 
| 486 527 | 
             
                        important_lines.extend(pattern.findall(text_without_code))
         | 
| 487 | 
            -
             | 
| 528 | 
            +
             | 
| 488 529 | 
             
                    # Build technical summary
         | 
| 489 530 | 
             
                    result_parts = []
         | 
| 490 | 
            -
             | 
| 531 | 
            +
             | 
| 491 532 | 
             
                    # Add imports/includes first
         | 
| 492 | 
            -
                    imports = [ | 
| 493 | 
            -
                         | 
| 494 | 
            -
             | 
| 533 | 
            +
                    imports = [
         | 
| 534 | 
            +
                        line
         | 
| 535 | 
            +
                        for line in important_lines
         | 
| 536 | 
            +
                        if any(
         | 
| 537 | 
            +
                            keyword in line
         | 
| 538 | 
            +
                            for keyword in ["import", "from", "require", "include", "using"]
         | 
| 539 | 
            +
                        )
         | 
| 540 | 
            +
                    ]
         | 
| 495 541 | 
             
                    if imports:
         | 
| 496 | 
            -
                        result_parts.append("# Imports/Dependencies\n" +  | 
| 497 | 
            -
             | 
| 542 | 
            +
                        result_parts.append("# Imports/Dependencies\n" + "\n".join(imports[:10]))
         | 
| 543 | 
            +
             | 
| 498 544 | 
             
                    # Add class/function definitions
         | 
| 499 | 
            -
                    definitions = [ | 
| 500 | 
            -
                         | 
| 501 | 
            -
             | 
| 545 | 
            +
                    definitions = [
         | 
| 546 | 
            +
                        line
         | 
| 547 | 
            +
                        for line in important_lines
         | 
| 548 | 
            +
                        if any(
         | 
| 549 | 
            +
                            keyword in line
         | 
| 550 | 
            +
                            for keyword in ["class", "def", "function", "interface", "struct"]
         | 
| 551 | 
            +
                        )
         | 
| 552 | 
            +
                    ]
         | 
| 502 553 | 
             
                    if definitions:
         | 
| 503 | 
            -
                        result_parts.append("# Key Definitions\n" +  | 
| 504 | 
            -
             | 
| 554 | 
            +
                        result_parts.append("# Key Definitions\n" + "\n".join(definitions[:20]))
         | 
| 555 | 
            +
             | 
| 505 556 | 
             
                    # Add some code blocks if space allows
         | 
| 506 557 | 
             
                    if preserve_code and code_blocks:
         | 
| 507 558 | 
             
                        result_parts.append("# Code Samples")
         | 
| 508 559 | 
             
                        for i, block in enumerate(code_blocks[:3]):  # Limit to first 3 blocks
         | 
| 509 | 
            -
                            if len( | 
| 560 | 
            +
                            if len("\n".join(result_parts)) + len(block) < max_chars * 0.8:
         | 
| 510 561 | 
             
                                result_parts.append(block)
         | 
| 511 | 
            -
             | 
| 512 | 
            -
                    result =  | 
| 513 | 
            -
             | 
| 562 | 
            +
             | 
| 563 | 
            +
                    result = "\n\n".join(result_parts)
         | 
| 564 | 
            +
             | 
| 514 565 | 
             
                    # If still too long, truncate
         | 
| 515 566 | 
             
                    if len(result) > max_chars:
         | 
| 516 567 | 
             
                        result = self._truncate_at_sentence(result, max_chars)
         | 
| 517 | 
            -
             | 
| 568 | 
            +
             | 
| 518 569 | 
             
                    return result
         | 
| 519 | 
            -
             | 
| 520 | 
            -
                def _process_chunks( | 
| 570 | 
            +
             | 
| 571 | 
            +
                def _process_chunks(
         | 
| 572 | 
            +
                    self, text: str, mode: str, max_chars_per_chunk: int, preserve_code: bool
         | 
| 573 | 
            +
                ) -> str:
         | 
| 521 574 | 
             
                    """
         | 
| 522 575 | 
             
                    Process large documents in chunks.
         | 
| 523 | 
            -
             | 
| 576 | 
            +
             | 
| 524 577 | 
             
                    WHY: Very large documents need to be processed in chunks to
         | 
| 525 578 | 
             
                    avoid memory issues and maintain performance.
         | 
| 526 579 | 
             
                    """
         | 
| 527 580 | 
             
                    chunks = []
         | 
| 528 581 | 
             
                    chunk_size = self.CHUNK_SIZE
         | 
| 529 | 
            -
             | 
| 582 | 
            +
             | 
| 530 583 | 
             
                    for i in range(0, len(text), chunk_size):
         | 
| 531 | 
            -
                        chunk = text[i:i + chunk_size]
         | 
| 532 | 
            -
             | 
| 584 | 
            +
                        chunk = text[i : i + chunk_size]
         | 
| 585 | 
            +
             | 
| 533 586 | 
             
                        # Summarize chunk based on mode
         | 
| 534 587 | 
             
                        if mode == "brief":
         | 
| 535 588 | 
             
                            summarized = self._summarize_brief(chunk, max_chars_per_chunk)
         | 
| @@ -538,26 +591,28 @@ class DocumentSummarizerTool(BaseToolAdapter): | |
| 538 591 | 
             
                        elif mode == "key_points":
         | 
| 539 592 | 
             
                            summarized = self._summarize_key_points(chunk, max_chars_per_chunk)
         | 
| 540 593 | 
             
                        elif mode == "technical":
         | 
| 541 | 
            -
                            summarized = self._summarize_technical( | 
| 594 | 
            +
                            summarized = self._summarize_technical(
         | 
| 595 | 
            +
                                chunk, max_chars_per_chunk, preserve_code
         | 
| 596 | 
            +
                            )
         | 
| 542 597 | 
             
                        else:
         | 
| 543 598 | 
             
                            summarized = self._summarize_brief(chunk, max_chars_per_chunk)
         | 
| 544 | 
            -
             | 
| 599 | 
            +
             | 
| 545 600 | 
             
                        chunks.append(summarized)
         | 
| 546 | 
            -
             | 
| 547 | 
            -
                    return  | 
| 548 | 
            -
             | 
| 601 | 
            +
             | 
| 602 | 
            +
                    return "\n\n[--- Next Section ---]\n\n".join(chunks)
         | 
| 603 | 
            +
             | 
| 549 604 | 
             
                async def invoke(self, invocation: MCPToolInvocation) -> MCPToolResult:
         | 
| 550 605 | 
             
                    """
         | 
| 551 606 | 
             
                    Invoke the document summarizer tool.
         | 
| 552 | 
            -
             | 
| 607 | 
            +
             | 
| 553 608 | 
             
                    Args:
         | 
| 554 609 | 
             
                        invocation: Tool invocation request
         | 
| 555 | 
            -
             | 
| 610 | 
            +
             | 
| 556 611 | 
             
                    Returns:
         | 
| 557 612 | 
             
                        Tool execution result with summary
         | 
| 558 613 | 
             
                    """
         | 
| 559 614 | 
             
                    start_time = datetime.now()
         | 
| 560 | 
            -
             | 
| 615 | 
            +
             | 
| 561 616 | 
             
                    try:
         | 
| 562 617 | 
             
                        # Get parameters
         | 
| 563 618 | 
             
                        file_path = invocation.parameters["file_path"]
         | 
| @@ -566,12 +621,12 @@ class DocumentSummarizerTool(BaseToolAdapter): | |
| 566 621 | 
             
                        max_percentage = invocation.parameters.get("max_percentage", 40)
         | 
| 567 622 | 
             
                        preserve_code = invocation.parameters.get("preserve_code", True)
         | 
| 568 623 | 
             
                        use_cache = invocation.parameters.get("use_cache", True)
         | 
| 569 | 
            -
             | 
| 624 | 
            +
             | 
| 570 625 | 
             
                        # Validate file
         | 
| 571 626 | 
             
                        is_valid, error_msg = self._validate_file(file_path)
         | 
| 572 627 | 
             
                        if not is_valid:
         | 
| 573 628 | 
             
                            raise ValueError(error_msg)
         | 
| 574 | 
            -
             | 
| 629 | 
            +
             | 
| 575 630 | 
             
                        # Check cache if enabled
         | 
| 576 631 | 
             
                        cache_hit = False
         | 
| 577 632 | 
             
                        if use_cache:
         | 
| @@ -581,35 +636,41 @@ class DocumentSummarizerTool(BaseToolAdapter): | |
| 581 636 | 
             
                                cache_hit = True
         | 
| 582 637 | 
             
                                execution_time = (datetime.now() - start_time).total_seconds()
         | 
| 583 638 | 
             
                                self._update_metrics(True, execution_time)
         | 
| 584 | 
            -
             | 
| 639 | 
            +
             | 
| 585 640 | 
             
                                return MCPToolResult(
         | 
| 586 641 | 
             
                                    success=True,
         | 
| 587 642 | 
             
                                    data={
         | 
| 588 643 | 
             
                                        **cached_result,
         | 
| 589 644 | 
             
                                        "cache_hit": True,
         | 
| 590 | 
            -
                                        "cache_stats": self._cache.get_stats()
         | 
| 645 | 
            +
                                        "cache_stats": self._cache.get_stats(),
         | 
| 591 646 | 
             
                                    },
         | 
| 592 647 | 
             
                                    execution_time=execution_time,
         | 
| 593 | 
            -
                                    metadata={ | 
| 648 | 
            +
                                    metadata={
         | 
| 649 | 
            +
                                        "tool": "document_summarizer",
         | 
| 650 | 
            +
                                        "mode": mode,
         | 
| 651 | 
            +
                                        "cached": True,
         | 
| 652 | 
            +
                                    },
         | 
| 594 653 | 
             
                                )
         | 
| 595 | 
            -
             | 
| 654 | 
            +
             | 
| 596 655 | 
             
                        # Read file content
         | 
| 597 656 | 
             
                        content = self._read_file(file_path)
         | 
| 598 657 | 
             
                        original_size = len(content)
         | 
| 599 | 
            -
             | 
| 658 | 
            +
             | 
| 600 659 | 
             
                        # Calculate target size
         | 
| 601 660 | 
             
                        if max_tokens:
         | 
| 602 661 | 
             
                            max_chars = max_tokens * self.CHARS_PER_TOKEN
         | 
| 603 662 | 
             
                        else:
         | 
| 604 663 | 
             
                            max_chars = int(original_size * (max_percentage / 100))
         | 
| 605 | 
            -
             | 
| 664 | 
            +
             | 
| 606 665 | 
             
                        # Process based on file size
         | 
| 607 666 | 
             
                        chunks_processed = 1
         | 
| 608 667 | 
             
                        if original_size > self.CHUNK_SIZE:
         | 
| 609 668 | 
             
                            # Process in chunks for large files
         | 
| 610 669 | 
             
                            chunks_processed = (original_size // self.CHUNK_SIZE) + 1
         | 
| 611 670 | 
             
                            max_chars_per_chunk = max_chars // chunks_processed
         | 
| 612 | 
            -
                            summary = self._process_chunks( | 
| 671 | 
            +
                            summary = self._process_chunks(
         | 
| 672 | 
            +
                                content, mode, max_chars_per_chunk, preserve_code
         | 
| 673 | 
            +
                            )
         | 
| 613 674 | 
             
                        else:
         | 
| 614 675 | 
             
                            # Process entire file
         | 
| 615 676 | 
             
                            if mode == "brief":
         | 
| @@ -619,19 +680,23 @@ class DocumentSummarizerTool(BaseToolAdapter): | |
| 619 680 | 
             
                            elif mode == "key_points":
         | 
| 620 681 | 
             
                                summary = self._summarize_key_points(content, max_chars)
         | 
| 621 682 | 
             
                            elif mode == "technical":
         | 
| 622 | 
            -
                                summary = self._summarize_technical( | 
| 683 | 
            +
                                summary = self._summarize_technical(
         | 
| 684 | 
            +
                                    content, max_chars, preserve_code
         | 
| 685 | 
            +
                                )
         | 
| 623 686 | 
             
                            else:
         | 
| 624 687 | 
             
                                summary = self._summarize_brief(content, max_chars)
         | 
| 625 | 
            -
             | 
| 688 | 
            +
             | 
| 626 689 | 
             
                        # Calculate metrics
         | 
| 627 690 | 
             
                        summary_size = len(summary)
         | 
| 628 | 
            -
                        reduction_percentage = ( | 
| 629 | 
            -
             | 
| 691 | 
            +
                        reduction_percentage = (
         | 
| 692 | 
            +
                            (original_size - summary_size) / original_size
         | 
| 693 | 
            +
                        ) * 100
         | 
| 694 | 
            +
             | 
| 630 695 | 
             
                        # Token estimates
         | 
| 631 696 | 
             
                        original_tokens = self._estimate_tokens(content)
         | 
| 632 697 | 
             
                        summary_tokens = self._estimate_tokens(summary)
         | 
| 633 698 | 
             
                        saved_tokens = original_tokens - summary_tokens
         | 
| 634 | 
            -
             | 
| 699 | 
            +
             | 
| 635 700 | 
             
                        # Prepare result
         | 
| 636 701 | 
             
                        result = {
         | 
| 637 702 | 
             
                            "summary": summary,
         | 
| @@ -641,26 +706,26 @@ class DocumentSummarizerTool(BaseToolAdapter): | |
| 641 706 | 
             
                            "token_estimate": {
         | 
| 642 707 | 
             
                                "original": original_tokens,
         | 
| 643 708 | 
             
                                "summary": summary_tokens,
         | 
| 644 | 
            -
                                "saved": saved_tokens
         | 
| 709 | 
            +
                                "saved": saved_tokens,
         | 
| 645 710 | 
             
                            },
         | 
| 646 711 | 
             
                            "chunks_processed": chunks_processed,
         | 
| 647 | 
            -
                            "cache_hit": cache_hit
         | 
| 712 | 
            +
                            "cache_hit": cache_hit,
         | 
| 648 713 | 
             
                        }
         | 
| 649 | 
            -
             | 
| 714 | 
            +
             | 
| 650 715 | 
             
                        # Cache result if enabled
         | 
| 651 716 | 
             
                        if use_cache and not cache_hit:
         | 
| 652 717 | 
             
                            cache_key = f"{self._get_file_hash(file_path)}:{mode}:{max_percentage}"
         | 
| 653 718 | 
             
                            self._cache.put(cache_key, result.copy(), summary_size)
         | 
| 654 | 
            -
             | 
| 719 | 
            +
             | 
| 655 720 | 
             
                        # Calculate execution time
         | 
| 656 721 | 
             
                        execution_time = (datetime.now() - start_time).total_seconds()
         | 
| 657 | 
            -
             | 
| 722 | 
            +
             | 
| 658 723 | 
             
                        # Update metrics
         | 
| 659 724 | 
             
                        self._update_metrics(True, execution_time)
         | 
| 660 | 
            -
             | 
| 725 | 
            +
             | 
| 661 726 | 
             
                        # Add cache stats to result
         | 
| 662 727 | 
             
                        result["cache_stats"] = self._cache.get_stats()
         | 
| 663 | 
            -
             | 
| 728 | 
            +
             | 
| 664 729 | 
             
                        return MCPToolResult(
         | 
| 665 730 | 
             
                            success=True,
         | 
| 666 731 | 
             
                            data=result,
         | 
| @@ -669,61 +734,64 @@ class DocumentSummarizerTool(BaseToolAdapter): | |
| 669 734 | 
             
                                "tool": "document_summarizer",
         | 
| 670 735 | 
             
                                "mode": mode,
         | 
| 671 736 | 
             
                                "file_path": file_path,
         | 
| 672 | 
            -
                                "reduction_achieved": reduction_percentage >= 60
         | 
| 673 | 
            -
                            }
         | 
| 737 | 
            +
                                "reduction_achieved": reduction_percentage >= 60,
         | 
| 738 | 
            +
                            },
         | 
| 674 739 | 
             
                        )
         | 
| 675 | 
            -
             | 
| 740 | 
            +
             | 
| 676 741 | 
             
                    except Exception as e:
         | 
| 677 742 | 
             
                        execution_time = (datetime.now() - start_time).total_seconds()
         | 
| 678 743 | 
             
                        self._update_metrics(False, execution_time)
         | 
| 679 744 | 
             
                        self._metrics["last_error"] = str(e)
         | 
| 680 | 
            -
             | 
| 745 | 
            +
             | 
| 681 746 | 
             
                        self.log_error(f"Document summarizer failed: {e}")
         | 
| 682 | 
            -
             | 
| 747 | 
            +
             | 
| 683 748 | 
             
                        return MCPToolResult(
         | 
| 684 749 | 
             
                            success=False,
         | 
| 685 750 | 
             
                            error=f"Document summarizer failed: {str(e)}",
         | 
| 686 751 | 
             
                            execution_time=execution_time,
         | 
| 687 | 
            -
                            metadata={ | 
| 752 | 
            +
                            metadata={
         | 
| 753 | 
            +
                                "tool": "document_summarizer",
         | 
| 754 | 
            +
                                "error_type": type(e).__name__,
         | 
| 755 | 
            +
                            },
         | 
| 688 756 | 
             
                        )
         | 
| 689 | 
            -
             | 
| 757 | 
            +
             | 
| 690 758 | 
             
                async def initialize(self) -> bool:
         | 
| 691 759 | 
             
                    """
         | 
| 692 760 | 
             
                    Initialize the document summarizer tool.
         | 
| 693 | 
            -
             | 
| 761 | 
            +
             | 
| 694 762 | 
             
                    Returns:
         | 
| 695 763 | 
             
                        True if initialization successful
         | 
| 696 764 | 
             
                    """
         | 
| 697 765 | 
             
                    try:
         | 
| 698 766 | 
             
                        self.log_info("Initializing document summarizer tool")
         | 
| 699 | 
            -
             | 
| 767 | 
            +
             | 
| 700 768 | 
             
                        # Clear cache on initialization
         | 
| 701 769 | 
             
                        self._cache = LRUCache(max_size=100, max_memory_mb=50)
         | 
| 702 | 
            -
             | 
| 770 | 
            +
             | 
| 703 771 | 
             
                        self._initialized = True
         | 
| 704 772 | 
             
                        self.log_info("Document summarizer tool initialized successfully")
         | 
| 705 773 | 
             
                        return True
         | 
| 706 | 
            -
             | 
| 774 | 
            +
             | 
| 707 775 | 
             
                    except Exception as e:
         | 
| 708 776 | 
             
                        self.log_error(f"Failed to initialize document summarizer: {e}")
         | 
| 709 777 | 
             
                        return False
         | 
| 710 | 
            -
             | 
| 778 | 
            +
             | 
| 711 779 | 
             
                async def shutdown(self) -> None:
         | 
| 712 780 | 
             
                    """
         | 
| 713 781 | 
             
                    Shutdown the document summarizer tool and clean up resources.
         | 
| 714 782 | 
             
                    """
         | 
| 715 783 | 
             
                    try:
         | 
| 716 784 | 
             
                        self.log_info("Shutting down document summarizer tool")
         | 
| 717 | 
            -
             | 
| 785 | 
            +
             | 
| 718 786 | 
             
                        # Log final cache stats
         | 
| 719 787 | 
             
                        cache_stats = self._cache.get_stats()
         | 
| 720 788 | 
             
                        self.log_info(f"Final cache stats: {cache_stats}")
         | 
| 721 | 
            -
             | 
| 789 | 
            +
             | 
| 722 790 | 
             
                        # Clear cache
         | 
| 723 791 | 
             
                        self._cache = None
         | 
| 724 | 
            -
             | 
| 792 | 
            +
             | 
| 725 793 | 
             
                        self._initialized = False
         | 
| 726 794 | 
             
                        self.log_info("Document summarizer tool shutdown complete")
         | 
| 727 | 
            -
             | 
| 795 | 
            +
             | 
| 728 796 | 
             
                    except Exception as e:
         | 
| 729 | 
            -
                        self.log_error(f"Error during document summarizer shutdown: {e}")
         | 
| 797 | 
            +
                        self.log_error(f"Error during document summarizer shutdown: {e}")
         |