claude-mpm 3.4.10__py3-none-any.whl → 5.4.85__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/BUILD_NUMBER +1 -0
- claude_mpm/VERSION +1 -0
- claude_mpm/__init__.py +50 -12
- claude_mpm/__main__.py +7 -2
- claude_mpm/agents/BASE_AGENT.md +164 -0
- claude_mpm/agents/BASE_ENGINEER.md +658 -0
- claude_mpm/agents/CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md +405 -0
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +112 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +186 -0
- claude_mpm/agents/MEMORY.md +72 -0
- claude_mpm/agents/PM_INSTRUCTIONS.md +1429 -0
- claude_mpm/agents/WORKFLOW.md +111 -0
- claude_mpm/agents/__init__.py +92 -80
- claude_mpm/agents/agent-template.yaml +83 -0
- claude_mpm/agents/agent_loader.py +560 -745
- claude_mpm/agents/agent_loader_integration.py +53 -55
- claude_mpm/agents/agents_metadata.py +186 -27
- claude_mpm/agents/async_agent_loader.py +436 -0
- claude_mpm/agents/base_agent.json +8 -4
- claude_mpm/agents/frontmatter_validator.py +754 -0
- claude_mpm/agents/system_agent_config.py +222 -155
- claude_mpm/agents/templates/README.md +465 -0
- claude_mpm/agents/templates/__init__.py +17 -13
- claude_mpm/agents/templates/circuit-breakers.md +1391 -0
- claude_mpm/agents/templates/context-management-examples.md +544 -0
- claude_mpm/agents/templates/git-file-tracking.md +584 -0
- claude_mpm/agents/templates/pm-examples.md +474 -0
- claude_mpm/agents/templates/pm-red-flags.md +310 -0
- claude_mpm/agents/templates/pr-workflow-examples.md +427 -0
- claude_mpm/agents/templates/research-gate-examples.md +669 -0
- claude_mpm/agents/templates/response-format.md +583 -0
- claude_mpm/agents/templates/structured-questions-examples.md +615 -0
- claude_mpm/agents/templates/ticket-completeness-examples.md +139 -0
- claude_mpm/agents/templates/ticketing-examples.md +277 -0
- claude_mpm/agents/templates/validation-templates.md +312 -0
- claude_mpm/cli/__init__.py +94 -128
- claude_mpm/cli/__main__.py +33 -0
- claude_mpm/cli/chrome_devtools_installer.py +175 -0
- claude_mpm/cli/commands/__init__.py +36 -12
- claude_mpm/cli/commands/agent_manager.py +1403 -0
- claude_mpm/cli/commands/agent_source.py +774 -0
- claude_mpm/cli/commands/agent_state_manager.py +335 -0
- claude_mpm/cli/commands/agents.py +2501 -168
- claude_mpm/cli/commands/agents_cleanup.py +210 -0
- claude_mpm/cli/commands/agents_discover.py +338 -0
- claude_mpm/cli/commands/agents_reconcile.py +197 -0
- claude_mpm/cli/commands/aggregate.py +540 -0
- claude_mpm/cli/commands/analyze.py +553 -0
- claude_mpm/cli/commands/analyze_code.py +528 -0
- claude_mpm/cli/commands/auto_configure.py +1053 -0
- claude_mpm/cli/commands/cleanup.py +588 -0
- claude_mpm/cli/commands/cleanup_orphaned_agents.py +150 -0
- claude_mpm/cli/commands/config.py +586 -0
- claude_mpm/cli/commands/configure.py +3253 -0
- claude_mpm/cli/commands/configure_agent_display.py +282 -0
- claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
- claude_mpm/cli/commands/configure_hook_manager.py +225 -0
- claude_mpm/cli/commands/configure_models.py +18 -0
- claude_mpm/cli/commands/configure_navigation.py +184 -0
- claude_mpm/cli/commands/configure_paths.py +104 -0
- claude_mpm/cli/commands/configure_persistence.py +254 -0
- claude_mpm/cli/commands/configure_startup_manager.py +646 -0
- claude_mpm/cli/commands/configure_template_editor.py +497 -0
- claude_mpm/cli/commands/configure_validators.py +73 -0
- claude_mpm/cli/commands/dashboard.py +286 -0
- claude_mpm/cli/commands/debug.py +1386 -0
- claude_mpm/cli/commands/doctor.py +243 -0
- claude_mpm/cli/commands/hook_errors.py +277 -0
- claude_mpm/cli/commands/info.py +195 -74
- claude_mpm/cli/commands/local_deploy.py +534 -0
- claude_mpm/cli/commands/mcp.py +205 -0
- claude_mpm/cli/commands/mcp_command_router.py +161 -0
- claude_mpm/cli/commands/mcp_config.py +154 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_external_commands.py +249 -0
- claude_mpm/cli/commands/mcp_install_commands.py +346 -0
- claude_mpm/cli/commands/mcp_pipx_config.py +208 -0
- claude_mpm/cli/commands/mcp_server_commands.py +155 -0
- claude_mpm/cli/commands/mcp_setup_external.py +868 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +585 -846
- claude_mpm/cli/commands/monitor.py +228 -310
- claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
- claude_mpm/cli/commands/mpm_init/core.py +759 -0
- claude_mpm/cli/commands/mpm_init/display.py +341 -0
- claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
- claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
- claude_mpm/cli/commands/mpm_init/modes.py +397 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +722 -0
- claude_mpm/cli/commands/mpm_init_cli.py +396 -0
- claude_mpm/cli/commands/mpm_init_handler.py +195 -0
- claude_mpm/cli/commands/postmortem.py +401 -0
- claude_mpm/cli/commands/profile.py +276 -0
- claude_mpm/cli/commands/run.py +910 -488
- claude_mpm/cli/commands/search.py +458 -0
- claude_mpm/cli/commands/skill_source.py +694 -0
- claude_mpm/cli/commands/skills.py +1398 -0
- claude_mpm/cli/commands/summarize.py +413 -0
- claude_mpm/cli/commands/tickets.py +536 -53
- claude_mpm/cli/commands/uninstall.py +176 -0
- claude_mpm/cli/commands/upgrade.py +152 -0
- claude_mpm/cli/commands/verify.py +119 -0
- claude_mpm/cli/executor.py +298 -0
- claude_mpm/cli/helpers.py +105 -0
- claude_mpm/cli/interactive/__init__.py +31 -0
- claude_mpm/cli/interactive/agent_wizard.py +1927 -0
- claude_mpm/cli/interactive/questionary_styles.py +65 -0
- claude_mpm/cli/interactive/skill_selector.py +481 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/parser.py +87 -563
- claude_mpm/cli/parsers/__init__.py +35 -0
- claude_mpm/cli/parsers/agent_manager_parser.py +393 -0
- claude_mpm/cli/parsers/agent_source_parser.py +171 -0
- claude_mpm/cli/parsers/agents_parser.py +575 -0
- claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
- claude_mpm/cli/parsers/analyze_parser.py +135 -0
- claude_mpm/cli/parsers/auto_configure_parser.py +120 -0
- claude_mpm/cli/parsers/base_parser.py +649 -0
- claude_mpm/cli/parsers/config_parser.py +208 -0
- claude_mpm/cli/parsers/configure_parser.py +138 -0
- claude_mpm/cli/parsers/dashboard_parser.py +113 -0
- claude_mpm/cli/parsers/debug_parser.py +319 -0
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/parsers/mcp_parser.py +195 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +142 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +311 -0
- claude_mpm/cli/parsers/profile_parser.py +147 -0
- claude_mpm/cli/parsers/run_parser.py +157 -0
- claude_mpm/cli/parsers/search_parser.py +245 -0
- claude_mpm/cli/parsers/skill_source_parser.py +169 -0
- claude_mpm/cli/parsers/skills_parser.py +277 -0
- claude_mpm/cli/parsers/source_parser.py +138 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/shared/__init__.py +40 -0
- claude_mpm/cli/shared/argument_patterns.py +205 -0
- claude_mpm/cli/shared/base_command.py +242 -0
- claude_mpm/cli/shared/error_handling.py +242 -0
- claude_mpm/cli/shared/output_formatters.py +241 -0
- claude_mpm/cli/startup.py +1578 -0
- claude_mpm/cli/startup_display.py +480 -0
- claude_mpm/cli/startup_logging.py +839 -0
- claude_mpm/cli/utils.py +136 -47
- 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 +42 -64
- claude_mpm/commands/__init__.py +14 -0
- claude_mpm/commands/mpm-config.md +28 -0
- claude_mpm/commands/mpm-doctor.md +20 -0
- claude_mpm/commands/mpm-help.md +20 -0
- claude_mpm/commands/mpm-init.md +120 -0
- claude_mpm/commands/mpm-monitor.md +31 -0
- claude_mpm/commands/mpm-organize.md +120 -0
- claude_mpm/commands/mpm-postmortem.md +21 -0
- claude_mpm/commands/mpm-session-resume.md +30 -0
- claude_mpm/commands/mpm-status.md +20 -0
- claude_mpm/commands/mpm-ticket-view.md +109 -0
- claude_mpm/commands/mpm-version.md +20 -0
- claude_mpm/commands/mpm.md +31 -0
- claude_mpm/config/__init__.py +42 -2
- claude_mpm/config/agent_config.py +402 -0
- claude_mpm/config/agent_presets.py +488 -0
- claude_mpm/config/agent_sources.py +352 -0
- claude_mpm/config/experimental_features.py +217 -0
- claude_mpm/config/model_config.py +428 -0
- claude_mpm/config/paths.py +258 -0
- claude_mpm/config/skill_presets.py +392 -0
- claude_mpm/config/skill_sources.py +590 -0
- claude_mpm/config/socketio_config.py +125 -83
- claude_mpm/constants.py +133 -22
- claude_mpm/core/__init__.py +62 -36
- claude_mpm/core/agent_name_normalizer.py +71 -73
- claude_mpm/core/agent_registry.py +385 -492
- claude_mpm/core/agent_session_manager.py +81 -70
- claude_mpm/core/api_validator.py +330 -0
- claude_mpm/core/base_service.py +159 -122
- claude_mpm/core/cache.py +560 -0
- claude_mpm/core/claude_runner.py +696 -916
- claude_mpm/core/config.py +613 -122
- claude_mpm/core/config_aliases.py +74 -73
- claude_mpm/core/config_constants.py +314 -0
- claude_mpm/core/constants.py +361 -0
- claude_mpm/core/container.py +646 -104
- claude_mpm/core/enums.py +452 -0
- claude_mpm/core/error_handler.py +623 -0
- claude_mpm/core/exceptions.py +536 -0
- claude_mpm/core/factories.py +105 -109
- claude_mpm/core/file_utils.py +764 -0
- claude_mpm/core/framework/__init__.py +25 -0
- claude_mpm/core/framework/formatters/__init__.py +11 -0
- claude_mpm/core/framework/formatters/capability_generator.py +367 -0
- claude_mpm/core/framework/formatters/content_formatter.py +278 -0
- claude_mpm/core/framework/formatters/context_generator.py +185 -0
- claude_mpm/core/framework/loaders/__init__.py +13 -0
- claude_mpm/core/framework/loaders/agent_loader.py +213 -0
- claude_mpm/core/framework/loaders/file_loader.py +176 -0
- claude_mpm/core/framework/loaders/instruction_loader.py +222 -0
- claude_mpm/core/framework/loaders/packaged_loader.py +232 -0
- claude_mpm/core/framework/processors/__init__.py +11 -0
- claude_mpm/core/framework/processors/memory_processor.py +230 -0
- claude_mpm/core/framework/processors/metadata_processor.py +146 -0
- claude_mpm/core/framework/processors/template_processor.py +244 -0
- claude_mpm/core/framework_loader.py +485 -414
- claude_mpm/core/hook_error_memory.py +381 -0
- claude_mpm/core/hook_manager.py +246 -86
- claude_mpm/core/hook_performance_config.py +147 -0
- claude_mpm/core/injectable_service.py +72 -63
- claude_mpm/core/instruction_reinforcement_hook.py +267 -0
- claude_mpm/core/interactive_session.py +670 -0
- claude_mpm/core/interfaces.py +570 -164
- claude_mpm/core/lazy.py +467 -0
- claude_mpm/core/log_manager.py +707 -0
- claude_mpm/core/logger.py +295 -134
- claude_mpm/core/logging_config.py +474 -0
- claude_mpm/core/logging_utils.py +520 -0
- claude_mpm/core/minimal_framework_loader.py +24 -22
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +594 -0
- claude_mpm/core/optimized_agent_loader.py +479 -0
- claude_mpm/core/optimized_startup.py +554 -0
- claude_mpm/core/output_style_manager.py +491 -0
- claude_mpm/core/pm_hook_interceptor.py +197 -82
- claude_mpm/core/protocols/__init__.py +23 -0
- claude_mpm/core/protocols/runner_protocol.py +103 -0
- claude_mpm/core/protocols/session_protocol.py +131 -0
- claude_mpm/core/service_registry.py +153 -116
- claude_mpm/core/session_manager.py +179 -64
- claude_mpm/core/shared/__init__.py +17 -0
- claude_mpm/core/shared/config_loader.py +326 -0
- claude_mpm/core/shared/path_resolver.py +281 -0
- claude_mpm/core/shared/singleton_manager.py +221 -0
- claude_mpm/core/socketio_pool.py +400 -137
- claude_mpm/core/system_context.py +38 -0
- claude_mpm/core/tool_access_control.py +64 -57
- claude_mpm/core/types.py +307 -0
- claude_mpm/core/typing_utils.py +553 -0
- claude_mpm/core/unified_agent_registry.py +969 -0
- claude_mpm/core/unified_config.py +612 -0
- claude_mpm/core/unified_paths.py +958 -0
- claude_mpm/dashboard/__init__.py +12 -0
- claude_mpm/dashboard/api/simple_directory.py +261 -0
- claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B0uc0UOD.js +36 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7RN905-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7xVLGWV.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BIF9m_hv.js +61 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BPYeabCQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BSNlmTZj.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Be7GpZd6.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bh0LDWpI.js +145 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BofRWZRR.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BovzEFCE.js +30 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C30mlcqg.js +165 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4B-KCzX.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4JcI4KD.js +122 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CBBdVcY8.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CDuw-vjf.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C_Usid8X.js +15 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cfqx1Qun.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CiIAseT4.js +128 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CnA0NrzZ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cs_tUR18.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CyWMqx4W.js +43 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzZX-COe.js +220 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzeYkLYB.js +65 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D3k0OPJN.js +4 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9lljYKQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DGkLK5U1.js +267 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DI7hHRFL.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DLVjFsZ3.js +139 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUrLdbGD.js +89 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DY1XQ8fi.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DZX00Y4g.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DaimHw_p.js +68 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +323 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dhb8PKl3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dle-35c7.js +64 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DmxopI1J.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DwBR2MJi.js +60 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/GYwsonyD.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/NqQ1dWOy.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/RJiighC3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Vzk33B_K.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ZGh7QtNv.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bT1r9zLR.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bTOqqlTd.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/eNVUfhuA.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/iEWssX7S.js +162 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/sQeU3Y1z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uuIeMWc-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.D6-I5TpK.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.m1gL8KXf.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.CgNOuw-d.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
- claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
- claude_mpm/dashboard/static/svelte-build/index.html +36 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
- claude_mpm/experimental/__init__.py +10 -0
- claude_mpm/experimental/cli_enhancements.py +104 -89
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +76 -66
- claude_mpm/hooks/__init__.py +37 -1
- claude_mpm/hooks/base_hook.py +37 -32
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +250 -0
- claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +888 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +652 -875
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +10 -7
- claude_mpm/hooks/claude_hooks/installer.py +806 -0
- claude_mpm/hooks/claude_hooks/memory_integration.py +249 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +412 -0
- claude_mpm/hooks/claude_hooks/services/__init__.py +15 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +229 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +254 -0
- claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +284 -0
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +224 -0
- claude_mpm/hooks/failure_learning/__init__.py +54 -0
- claude_mpm/hooks/failure_learning/failure_detection_hook.py +230 -0
- claude_mpm/hooks/failure_learning/fix_detection_hook.py +212 -0
- claude_mpm/hooks/failure_learning/learning_extraction_hook.py +281 -0
- claude_mpm/hooks/instruction_reinforcement.py +301 -0
- claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
- claude_mpm/hooks/kuzu_memory_hook.py +386 -0
- claude_mpm/hooks/kuzu_response_hook.py +179 -0
- claude_mpm/hooks/memory_integration_hook.py +201 -107
- claude_mpm/hooks/session_resume_hook.py +121 -0
- claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
- claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
- claude_mpm/hooks/tool_call_interceptor.py +92 -76
- claude_mpm/hooks/validation_hooks.py +62 -54
- claude_mpm/init.py +518 -83
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +40 -23
- claude_mpm/models/agent_session.py +538 -0
- claude_mpm/models/git_repository.py +198 -0
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/schemas/__init__.py +12 -0
- claude_mpm/scripts/__init__.py +15 -0
- claude_mpm/scripts/claude-hook-handler.sh +227 -0
- claude_mpm/scripts/launch_monitor.py +165 -0
- claude_mpm/scripts/mpm_doctor.py +322 -0
- claude_mpm/scripts/socketio_daemon.py +189 -200
- claude_mpm/scripts/start_activity_logging.py +91 -0
- claude_mpm/services/__init__.py +208 -39
- claude_mpm/services/agent_capabilities_service.py +266 -0
- claude_mpm/services/agents/__init__.py +89 -0
- claude_mpm/services/agents/agent_builder.py +514 -0
- claude_mpm/services/agents/agent_preset_service.py +238 -0
- claude_mpm/services/agents/agent_recommendation_service.py +278 -0
- claude_mpm/services/agents/agent_review_service.py +280 -0
- claude_mpm/services/agents/agent_selection_service.py +484 -0
- claude_mpm/services/agents/auto_config_manager.py +796 -0
- claude_mpm/services/agents/auto_deploy_index_parser.py +569 -0
- claude_mpm/services/agents/cache_git_manager.py +621 -0
- claude_mpm/services/agents/deployment/__init__.py +21 -0
- claude_mpm/services/agents/deployment/agent_config_provider.py +410 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +358 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +80 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +1037 -0
- claude_mpm/services/agents/deployment/agent_discovery_service.py +546 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +288 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +383 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +505 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +160 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +957 -0
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +273 -0
- claude_mpm/services/agents/deployment/agent_operation_service.py +573 -0
- claude_mpm/services/agents/deployment/agent_record_service.py +418 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +84 -0
- claude_mpm/services/agents/deployment/agent_state_service.py +381 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +1377 -0
- claude_mpm/services/agents/deployment/agent_validator.py +376 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +322 -0
- claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +10 -13
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +149 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +768 -0
- claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +181 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +178 -0
- claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
- claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +120 -0
- claude_mpm/services/agents/deployment/deployment_wrapper.py +129 -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 +70 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +269 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +226 -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/local_template_deployment.py +362 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +1478 -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 +162 -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 +240 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +110 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +80 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +92 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +101 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +102 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +269 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +311 -0
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +862 -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/single_agent_deployer.py +315 -0
- claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +113 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +148 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +131 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +130 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +228 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +21 -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 +319 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +214 -0
- claude_mpm/services/agents/git_source_manager.py +682 -0
- claude_mpm/services/agents/loading/__init__.py +11 -0
- claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +306 -228
- claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +106 -91
- claude_mpm/services/agents/loading/framework_agent_loader.py +433 -0
- claude_mpm/services/agents/local_template_manager.py +784 -0
- claude_mpm/services/agents/management/__init__.py +9 -0
- claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +92 -69
- claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +219 -168
- claude_mpm/services/agents/memory/__init__.py +22 -0
- claude_mpm/services/agents/memory/agent_memory_manager.py +784 -0
- claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +20 -18
- claude_mpm/services/agents/memory/content_manager.py +470 -0
- claude_mpm/services/agents/memory/memory_categorization_service.py +167 -0
- claude_mpm/services/agents/memory/memory_file_service.py +129 -0
- claude_mpm/services/agents/memory/memory_format_service.py +201 -0
- claude_mpm/services/agents/memory/memory_limits_service.py +101 -0
- claude_mpm/services/agents/memory/template_generator.py +83 -0
- claude_mpm/services/agents/observers.py +547 -0
- claude_mpm/services/agents/recommender.py +617 -0
- claude_mpm/services/agents/registry/__init__.py +30 -0
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +273 -0
- claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +370 -295
- claude_mpm/services/agents/single_tier_deployment_service.py +696 -0
- claude_mpm/services/agents/sources/__init__.py +13 -0
- claude_mpm/services/agents/sources/agent_sync_state.py +516 -0
- claude_mpm/services/agents/sources/git_source_sync_service.py +1205 -0
- claude_mpm/services/agents/startup_sync.py +262 -0
- claude_mpm/services/agents/toolchain_detector.py +478 -0
- claude_mpm/services/analysis/__init__.py +35 -0
- claude_mpm/services/analysis/clone_detector.py +1030 -0
- claude_mpm/services/analysis/postmortem_reporter.py +474 -0
- claude_mpm/services/analysis/postmortem_service.py +765 -0
- claude_mpm/services/async_session_logger.py +665 -0
- claude_mpm/services/claude_session_logger.py +321 -0
- claude_mpm/services/cli/__init__.py +18 -0
- claude_mpm/services/cli/agent_cleanup_service.py +408 -0
- claude_mpm/services/cli/agent_dependency_service.py +395 -0
- claude_mpm/services/cli/agent_listing_service.py +463 -0
- claude_mpm/services/cli/agent_output_formatter.py +605 -0
- claude_mpm/services/cli/agent_validation_service.py +590 -0
- claude_mpm/services/cli/memory_crud_service.py +622 -0
- claude_mpm/services/cli/memory_output_formatter.py +604 -0
- claude_mpm/services/cli/resume_service.py +617 -0
- claude_mpm/services/cli/session_manager.py +604 -0
- claude_mpm/services/cli/session_pause_manager.py +504 -0
- claude_mpm/services/cli/session_resume_helper.py +372 -0
- claude_mpm/services/cli/startup_checker.py +362 -0
- claude_mpm/services/cli/unified_dashboard_manager.py +439 -0
- claude_mpm/services/command_deployment_service.py +446 -0
- claude_mpm/services/command_handler_service.py +221 -0
- claude_mpm/services/communication/__init__.py +22 -0
- claude_mpm/services/core/__init__.py +108 -0
- claude_mpm/services/core/base.py +269 -0
- claude_mpm/services/core/cache_manager.py +309 -0
- claude_mpm/services/core/interfaces/__init__.py +273 -0
- claude_mpm/services/core/interfaces/agent.py +514 -0
- claude_mpm/services/core/interfaces/communication.py +316 -0
- claude_mpm/services/core/interfaces/health.py +169 -0
- claude_mpm/services/core/interfaces/infrastructure.py +357 -0
- claude_mpm/services/core/interfaces/model.py +281 -0
- claude_mpm/services/core/interfaces/process.py +372 -0
- claude_mpm/services/core/interfaces/project.py +121 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/service.py +405 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/interfaces.py +81 -0
- claude_mpm/services/core/memory_manager.py +682 -0
- claude_mpm/services/core/models/__init__.py +70 -0
- claude_mpm/services/core/models/agent_config.py +384 -0
- claude_mpm/services/core/models/health.py +162 -0
- claude_mpm/services/core/models/process.py +239 -0
- claude_mpm/services/core/models/restart.py +302 -0
- claude_mpm/services/core/models/stability.py +264 -0
- claude_mpm/services/core/models/toolchain.py +306 -0
- claude_mpm/services/core/path_resolver.py +517 -0
- claude_mpm/services/core/service_container.py +520 -0
- claude_mpm/services/core/service_interfaces.py +436 -0
- claude_mpm/services/diagnostics/__init__.py +18 -0
- claude_mpm/services/diagnostics/checks/__init__.py +38 -0
- claude_mpm/services/diagnostics/checks/agent_check.py +370 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +577 -0
- claude_mpm/services/diagnostics/checks/base_check.py +60 -0
- claude_mpm/services/diagnostics/checks/claude_code_check.py +270 -0
- claude_mpm/services/diagnostics/checks/common_issues_check.py +363 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +306 -0
- claude_mpm/services/diagnostics/checks/filesystem_check.py +233 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +520 -0
- claude_mpm/services/diagnostics/checks/instructions_check.py +415 -0
- claude_mpm/services/diagnostics/checks/mcp_check.py +330 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +1058 -0
- claude_mpm/services/diagnostics/checks/monitor_check.py +281 -0
- claude_mpm/services/diagnostics/checks/skill_sources_check.py +587 -0
- claude_mpm/services/diagnostics/checks/startup_log_check.py +319 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +286 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +578 -0
- claude_mpm/services/diagnostics/models.py +138 -0
- claude_mpm/services/event_aggregator.py +582 -0
- claude_mpm/services/event_bus/__init__.py +18 -0
- claude_mpm/services/event_bus/config.py +186 -0
- claude_mpm/services/event_bus/direct_relay.py +312 -0
- claude_mpm/services/event_bus/event_bus.py +396 -0
- claude_mpm/services/event_bus/relay.py +326 -0
- claude_mpm/services/events/__init__.py +44 -0
- claude_mpm/services/events/consumers/__init__.py +18 -0
- claude_mpm/services/events/consumers/dead_letter.py +306 -0
- claude_mpm/services/events/consumers/logging.py +184 -0
- claude_mpm/services/events/consumers/metrics.py +241 -0
- claude_mpm/services/events/consumers/socketio.py +377 -0
- claude_mpm/services/events/core.py +480 -0
- claude_mpm/services/events/interfaces.py +214 -0
- claude_mpm/services/events/producers/__init__.py +14 -0
- claude_mpm/services/events/producers/hook.py +269 -0
- claude_mpm/services/events/producers/system.py +329 -0
- claude_mpm/services/exceptions.py +433 -353
- claude_mpm/services/framework_claude_md_generator/__init__.py +81 -80
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +74 -67
- claude_mpm/services/framework_claude_md_generator/content_validator.py +66 -62
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +82 -60
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +36 -37
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +41 -40
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +15 -15
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +5 -4
- 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 +5 -4
- 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 +26 -30
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +31 -30
- claude_mpm/services/git/__init__.py +21 -0
- claude_mpm/services/git/git_operations_service.py +579 -0
- claude_mpm/services/github/__init__.py +21 -0
- claude_mpm/services/github/github_cli_service.py +397 -0
- claude_mpm/services/hook_installer_service.py +506 -0
- claude_mpm/services/hook_service.py +159 -111
- claude_mpm/services/infrastructure/__init__.py +52 -0
- claude_mpm/services/infrastructure/context_preservation.py +569 -0
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +209 -0
- claude_mpm/services/infrastructure/monitoring/__init__.py +39 -0
- claude_mpm/services/infrastructure/monitoring/aggregator.py +432 -0
- claude_mpm/services/infrastructure/monitoring/base.py +122 -0
- claude_mpm/services/infrastructure/monitoring/legacy.py +203 -0
- claude_mpm/services/infrastructure/monitoring/network.py +219 -0
- claude_mpm/services/infrastructure/monitoring/process.py +343 -0
- claude_mpm/services/infrastructure/monitoring/resources.py +244 -0
- claude_mpm/services/infrastructure/monitoring/service.py +368 -0
- claude_mpm/services/infrastructure/monitoring.py +71 -0
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/instructions/__init__.py +9 -0
- claude_mpm/services/instructions/instruction_cache_service.py +374 -0
- claude_mpm/services/local_ops/__init__.py +155 -0
- claude_mpm/services/local_ops/crash_detector.py +257 -0
- claude_mpm/services/local_ops/health_checks/__init__.py +26 -0
- claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
- claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
- claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
- claude_mpm/services/local_ops/health_manager.py +427 -0
- claude_mpm/services/local_ops/log_monitor.py +396 -0
- claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
- claude_mpm/services/local_ops/process_manager.py +595 -0
- claude_mpm/services/local_ops/resource_monitor.py +331 -0
- claude_mpm/services/local_ops/restart_manager.py +401 -0
- claude_mpm/services/local_ops/restart_policy.py +387 -0
- claude_mpm/services/local_ops/state_manager.py +372 -0
- claude_mpm/services/local_ops/unified_manager.py +600 -0
- claude_mpm/services/mcp_config_manager.py +1542 -0
- claude_mpm/services/mcp_service_verifier.py +732 -0
- claude_mpm/services/memory/__init__.py +19 -0
- claude_mpm/services/{memory_builder.py → memory/builder.py} +465 -373
- claude_mpm/services/memory/cache/__init__.py +14 -0
- claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +237 -200
- claude_mpm/services/memory/cache/simple_cache.py +331 -0
- claude_mpm/services/memory/failure_tracker.py +578 -0
- claude_mpm/services/memory/indexed_memory.py +648 -0
- claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +272 -243
- claude_mpm/services/memory/router.py +951 -0
- claude_mpm/services/memory_hook_service.py +470 -0
- claude_mpm/services/model/__init__.py +147 -0
- claude_mpm/services/model/base_provider.py +365 -0
- claude_mpm/services/model/claude_provider.py +412 -0
- claude_mpm/services/model/model_router.py +452 -0
- claude_mpm/services/model/ollama_provider.py +415 -0
- claude_mpm/services/monitor/__init__.py +20 -0
- claude_mpm/services/monitor/daemon.py +698 -0
- claude_mpm/services/monitor/daemon_manager.py +1076 -0
- claude_mpm/services/monitor/event_emitter.py +350 -0
- claude_mpm/services/monitor/handlers/__init__.py +21 -0
- claude_mpm/services/monitor/handlers/code_analysis.py +332 -0
- claude_mpm/services/monitor/handlers/dashboard.py +299 -0
- claude_mpm/services/monitor/handlers/file.py +264 -0
- claude_mpm/services/monitor/handlers/hooks.py +512 -0
- claude_mpm/services/monitor/management/__init__.py +18 -0
- claude_mpm/services/monitor/management/health.py +124 -0
- claude_mpm/services/monitor/management/lifecycle.py +730 -0
- claude_mpm/services/monitor/server.py +1493 -0
- claude_mpm/services/monitor_build_service.py +349 -0
- claude_mpm/services/native_agent_converter.py +356 -0
- claude_mpm/services/orphan_detection.py +786 -0
- claude_mpm/services/pm_skills_deployer.py +711 -0
- claude_mpm/services/port_manager.py +597 -0
- claude_mpm/services/pr/__init__.py +14 -0
- claude_mpm/services/pr/pr_template_service.py +329 -0
- claude_mpm/services/profile_manager.py +337 -0
- claude_mpm/services/project/__init__.py +44 -0
- claude_mpm/services/{project_analyzer.py → project/analyzer.py} +541 -291
- claude_mpm/services/project/analyzer_v2.py +566 -0
- claude_mpm/services/project/architecture_analyzer.py +461 -0
- claude_mpm/services/project/archive_manager.py +1045 -0
- claude_mpm/services/project/dependency_analyzer.py +462 -0
- claude_mpm/services/project/detection_strategies.py +719 -0
- claude_mpm/services/project/documentation_manager.py +554 -0
- claude_mpm/services/project/enhanced_analyzer.py +572 -0
- claude_mpm/services/project/language_analyzer.py +265 -0
- claude_mpm/services/project/metrics_collector.py +407 -0
- claude_mpm/services/project/project_organizer.py +1009 -0
- claude_mpm/services/project/registry.py +636 -0
- claude_mpm/services/project/toolchain_analyzer.py +583 -0
- claude_mpm/services/project_port_allocator.py +596 -0
- claude_mpm/services/recovery_manager.py +293 -240
- claude_mpm/services/response_tracker.py +267 -0
- claude_mpm/services/runner_configuration_service.py +605 -0
- claude_mpm/services/self_upgrade_service.py +608 -0
- claude_mpm/services/session_management_service.py +314 -0
- claude_mpm/services/session_manager.py +380 -0
- claude_mpm/services/shared/__init__.py +21 -0
- claude_mpm/services/shared/async_service_base.py +216 -0
- claude_mpm/services/shared/config_service_base.py +301 -0
- claude_mpm/services/shared/lifecycle_service_base.py +308 -0
- claude_mpm/services/shared/manager_base.py +315 -0
- claude_mpm/services/shared/service_factory.py +309 -0
- claude_mpm/services/skills/__init__.py +21 -0
- claude_mpm/services/skills/git_skill_source_manager.py +1340 -0
- claude_mpm/services/skills/selective_skill_deployer.py +743 -0
- claude_mpm/services/skills/skill_discovery_service.py +568 -0
- claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
- claude_mpm/services/skills_config.py +547 -0
- claude_mpm/services/skills_deployer.py +1168 -0
- claude_mpm/services/socketio/__init__.py +25 -0
- claude_mpm/services/socketio/client_proxy.py +229 -0
- claude_mpm/services/socketio/dashboard_server.py +362 -0
- claude_mpm/services/socketio/event_normalizer.py +798 -0
- claude_mpm/services/socketio/handlers/__init__.py +30 -0
- claude_mpm/services/socketio/handlers/base.py +136 -0
- claude_mpm/services/socketio/handlers/code_analysis.py +682 -0
- claude_mpm/services/socketio/handlers/connection.py +643 -0
- claude_mpm/services/socketio/handlers/connection_handler.py +333 -0
- claude_mpm/services/socketio/handlers/file.py +263 -0
- claude_mpm/services/socketio/handlers/git.py +962 -0
- claude_mpm/services/socketio/handlers/hook.py +211 -0
- claude_mpm/services/socketio/handlers/memory.py +26 -0
- claude_mpm/services/socketio/handlers/project.py +24 -0
- claude_mpm/services/socketio/handlers/registry.py +214 -0
- claude_mpm/services/socketio/migration_utils.py +343 -0
- claude_mpm/services/socketio/monitor_client.py +364 -0
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +569 -0
- claude_mpm/services/socketio/server/connection_manager.py +579 -0
- claude_mpm/services/socketio/server/core.py +1079 -0
- claude_mpm/services/socketio/server/eventbus_integration.py +245 -0
- claude_mpm/services/socketio/server/main.py +501 -0
- claude_mpm/services/socketio_client_manager.py +173 -143
- claude_mpm/services/socketio_server.py +38 -1657
- claude_mpm/services/subprocess_launcher_service.py +322 -0
- claude_mpm/services/system_instructions_service.py +270 -0
- claude_mpm/services/ticket_manager.py +25 -209
- claude_mpm/services/ticket_services/__init__.py +26 -0
- claude_mpm/services/ticket_services/crud_service.py +328 -0
- claude_mpm/services/ticket_services/formatter_service.py +290 -0
- claude_mpm/services/ticket_services/search_service.py +324 -0
- claude_mpm/services/ticket_services/validation_service.py +303 -0
- claude_mpm/services/ticket_services/workflow_service.py +244 -0
- claude_mpm/services/unified/__init__.py +65 -0
- claude_mpm/services/unified/analyzer_strategies/__init__.py +44 -0
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +518 -0
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +680 -0
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +900 -0
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +745 -0
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +733 -0
- claude_mpm/services/unified/config_strategies/__init__.py +175 -0
- claude_mpm/services/unified/config_strategies/config_schema.py +731 -0
- claude_mpm/services/unified/config_strategies/context_strategy.py +747 -0
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +1005 -0
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +881 -0
- claude_mpm/services/unified/config_strategies/unified_config_service.py +823 -0
- claude_mpm/services/unified/config_strategies/validation_strategy.py +1148 -0
- claude_mpm/services/unified/deployment_strategies/__init__.py +97 -0
- claude_mpm/services/unified/deployment_strategies/base.py +553 -0
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +573 -0
- claude_mpm/services/unified/deployment_strategies/local.py +607 -0
- claude_mpm/services/unified/deployment_strategies/utils.py +667 -0
- claude_mpm/services/unified/deployment_strategies/vercel.py +471 -0
- claude_mpm/services/unified/interfaces.py +475 -0
- claude_mpm/services/unified/migration.py +509 -0
- claude_mpm/services/unified/strategies.py +534 -0
- claude_mpm/services/unified/unified_analyzer.py +542 -0
- claude_mpm/services/unified/unified_config.py +691 -0
- claude_mpm/services/unified/unified_deployment.py +466 -0
- claude_mpm/services/utility_service.py +280 -0
- claude_mpm/services/version_control/__init__.py +34 -37
- claude_mpm/services/version_control/branch_strategy.py +26 -17
- claude_mpm/services/version_control/conflict_resolution.py +52 -36
- claude_mpm/services/version_control/git_operations.py +183 -49
- claude_mpm/services/version_control/semantic_versioning.py +172 -61
- claude_mpm/services/version_control/version_parser.py +546 -0
- claude_mpm/services/version_service.py +379 -0
- claude_mpm/services/visualization/__init__.py +15 -0
- claude_mpm/services/visualization/mermaid_generator.py +937 -0
- claude_mpm/skills/__init__.py +42 -0
- claude_mpm/skills/agent_skills_injector.py +324 -0
- claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
- claude_mpm/skills/bundled/__init__.py +6 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
- claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
- claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +573 -0
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
- claude_mpm/skills/bundled/pm/pm-bug-reporting/pm-bug-reporting.md +248 -0
- claude_mpm/skills/bundled/pm/pm-delegation-patterns/SKILL.md +167 -0
- claude_mpm/skills/bundled/pm/pm-git-file-tracking/SKILL.md +113 -0
- claude_mpm/skills/bundled/pm/pm-pr-workflow/SKILL.md +124 -0
- claude_mpm/skills/bundled/pm/pm-teaching-mode/SKILL.md +657 -0
- claude_mpm/skills/bundled/pm/pm-ticketing-integration/SKILL.md +154 -0
- claude_mpm/skills/bundled/pm/pm-verification-protocols/SKILL.md +198 -0
- claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
- claude_mpm/skills/bundled/security-scanning.md +439 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
- claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
- claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
- claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
- claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
- claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
- claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
- claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
- claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
- claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
- claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
- claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +286 -0
- claude_mpm/skills/skill_manager.py +405 -0
- claude_mpm/skills/skills_registry.py +347 -0
- claude_mpm/skills/skills_service.py +739 -0
- claude_mpm/storage/__init__.py +9 -0
- claude_mpm/storage/state_storage.py +546 -0
- claude_mpm/templates/.pre-commit-config.yaml +112 -0
- claude_mpm/templates/questions/__init__.py +38 -0
- claude_mpm/templates/questions/base.py +193 -0
- claude_mpm/templates/questions/pr_strategy.py +311 -0
- claude_mpm/templates/questions/project_init.py +385 -0
- claude_mpm/templates/questions/ticket_mgmt.py +394 -0
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/tools/__init__.py +10 -0
- claude_mpm/tools/__main__.py +208 -0
- claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
- claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
- claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
- claude_mpm/tools/code_tree_analyzer/core.py +380 -0
- claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
- claude_mpm/tools/code_tree_analyzer/events.py +168 -0
- claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
- claude_mpm/tools/code_tree_analyzer/models.py +39 -0
- claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
- claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
- claude_mpm/tools/code_tree_builder.py +631 -0
- claude_mpm/tools/code_tree_events.py +420 -0
- claude_mpm/tools/socketio_debug.py +671 -0
- claude_mpm/utils/__init__.py +8 -8
- claude_mpm/utils/agent_dependency_loader.py +1189 -0
- claude_mpm/utils/agent_filters.py +261 -0
- claude_mpm/utils/common.py +544 -0
- claude_mpm/utils/config_manager.py +168 -126
- claude_mpm/utils/console.py +11 -0
- claude_mpm/utils/database_connector.py +298 -0
- claude_mpm/utils/dependency_cache.py +373 -0
- claude_mpm/utils/dependency_manager.py +60 -59
- claude_mpm/utils/dependency_strategies.py +381 -0
- claude_mpm/utils/display_helper.py +260 -0
- claude_mpm/utils/environment_context.py +313 -0
- claude_mpm/utils/error_handler.py +78 -66
- claude_mpm/utils/file_utils.py +305 -0
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/git_analyzer.py +407 -0
- claude_mpm/utils/gitignore.py +244 -0
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/log_cleanup.py +627 -0
- claude_mpm/utils/migration.py +372 -0
- claude_mpm/utils/path_operations.py +110 -104
- claude_mpm/utils/progress.py +387 -0
- claude_mpm/utils/robust_installer.py +844 -0
- claude_mpm/utils/session_logging.py +121 -0
- claude_mpm/utils/structured_questions.py +619 -0
- claude_mpm/utils/subprocess_utils.py +343 -0
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +214 -108
- claude_mpm/validation/frontmatter_validator.py +252 -0
- claude_mpm-5.4.85.dist-info/METADATA +1023 -0
- claude_mpm-5.4.85.dist-info/RECORD +980 -0
- {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.85.dist-info}/entry_points.txt +1 -3
- claude_mpm-5.4.85.dist-info/licenses/LICENSE +94 -0
- claude_mpm-5.4.85.dist-info/licenses/LICENSE-FAQ.md +153 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -88
- claude_mpm/agents/INSTRUCTIONS.md +0 -352
- claude_mpm/agents/backups/INSTRUCTIONS.md +0 -352
- claude_mpm/agents/base_agent_loader.py +0 -529
- claude_mpm/agents/schema/agent_schema.json +0 -314
- claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -36
- claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +0 -46
- claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +0 -45
- claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +0 -49
- claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +0 -46
- claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +0 -45
- claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +0 -49
- claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +0 -46
- claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +0 -46
- claude_mpm/agents/templates/data_engineer.json +0 -110
- claude_mpm/agents/templates/documentation.json +0 -109
- claude_mpm/agents/templates/engineer.json +0 -113
- claude_mpm/agents/templates/ops.json +0 -109
- claude_mpm/agents/templates/pm.json +0 -25
- claude_mpm/agents/templates/qa.json +0 -111
- claude_mpm/agents/templates/research.json +0 -65
- claude_mpm/agents/templates/security.json +0 -113
- claude_mpm/agents/templates/test_integration.json +0 -112
- claude_mpm/agents/templates/version_control.json +0 -107
- claude_mpm/cli/commands/ui.py +0 -57
- claude_mpm/core/simple_runner.py +0 -1046
- claude_mpm/dashboard/open_dashboard.py +0 -34
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/builtin/__init__.py +0 -1
- claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
- claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
- claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
- claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
- claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
- claude_mpm/orchestration/__init__.py +0 -6
- claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
- claude_mpm/orchestration/archive/factory.py +0 -215
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
- claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
- claude_mpm/orchestration/archive/orchestrator.py +0 -501
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
- claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
- claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
- claude_mpm/schemas/workflow_validator.py +0 -411
- claude_mpm/services/agent_deployment.py +0 -1534
- claude_mpm/services/agent_lifecycle_manager.py +0 -1169
- claude_mpm/services/agent_memory_manager.py +0 -1415
- claude_mpm/services/agent_registry.py +0 -676
- claude_mpm/services/deployed_agent_discovery.py +0 -226
- claude_mpm/services/framework_agent_loader.py +0 -337
- claude_mpm/services/framework_claude_md_generator.py +0 -621
- claude_mpm/services/health_monitor.py +0 -892
- claude_mpm/services/memory_router.py +0 -538
- claude_mpm/services/parent_directory_manager/__init__.py +0 -577
- claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
- claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
- claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
- claude_mpm/services/parent_directory_manager/operations.py +0 -186
- claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
- claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
- claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
- claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
- claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
- 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 -508
- claude_mpm/ui/__init__.py +0 -1
- claude_mpm/ui/rich_terminal_ui.py +0 -295
- claude_mpm/ui/terminal_ui.py +0 -328
- claude_mpm/utils/paths.py +0 -289
- claude_mpm-3.4.10.dist-info/METADATA +0 -183
- claude_mpm-3.4.10.dist-info/RECORD +0 -201
- claude_mpm-3.4.10.dist-info/licenses/LICENSE +0 -21
- {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.85.dist-info}/WHEEL +0 -0
- {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.85.dist-info}/top_level.txt +0 -0
|
@@ -1,932 +1,709 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
2
|
+
"""Refactored Claude Code hook handler with modular service architecture.
|
|
3
3
|
|
|
4
|
-
This handler
|
|
5
|
-
|
|
4
|
+
This handler uses a service-oriented architecture with:
|
|
5
|
+
- StateManagerService: Manages state and delegation tracking
|
|
6
|
+
- ConnectionManagerService: Handles SocketIO connections with HTTP fallback
|
|
7
|
+
- SubagentResponseProcessor: Processes complex subagent responses
|
|
8
|
+
- DuplicateEventDetector: Detects and filters duplicate events
|
|
6
9
|
|
|
7
|
-
WHY
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
10
|
+
WHY service-oriented approach:
|
|
11
|
+
- Better separation of concerns and modularity
|
|
12
|
+
- Easier testing and maintenance
|
|
13
|
+
- Reduced file size from 1040 to ~400 lines
|
|
14
|
+
- Clear service boundaries and responsibilities
|
|
15
|
+
|
|
16
|
+
NOTE: Requires Claude Code version 1.0.92 or higher for proper hook support.
|
|
17
|
+
Earlier versions do not support matcher-based hook configuration.
|
|
13
18
|
"""
|
|
14
19
|
|
|
15
20
|
import json
|
|
16
|
-
import sys
|
|
17
21
|
import os
|
|
22
|
+
import re
|
|
23
|
+
import select
|
|
24
|
+
import signal
|
|
18
25
|
import subprocess
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
from
|
|
22
|
-
|
|
23
|
-
# Quick environment check - must be defined before any code that might use it
|
|
24
|
-
DEBUG = os.environ.get('CLAUDE_MPM_HOOK_DEBUG', '').lower() == 'true'
|
|
26
|
+
import sys
|
|
27
|
+
import threading
|
|
28
|
+
from datetime import datetime, timezone
|
|
29
|
+
from typing import Optional, Tuple
|
|
25
30
|
|
|
26
|
-
#
|
|
27
|
-
MEMORY_HOOKS_AVAILABLE = False
|
|
31
|
+
# Import extracted modules with fallback for direct execution
|
|
28
32
|
try:
|
|
29
|
-
# Try
|
|
30
|
-
import
|
|
33
|
+
# Try relative imports first (when imported as module)
|
|
34
|
+
from .event_handlers import EventHandlers
|
|
35
|
+
from .memory_integration import MemoryHookManager
|
|
36
|
+
from .response_tracking import ResponseTrackingManager
|
|
37
|
+
from .services import (
|
|
38
|
+
ConnectionManagerService,
|
|
39
|
+
DuplicateEventDetector,
|
|
40
|
+
StateManagerService,
|
|
41
|
+
SubagentResponseProcessor,
|
|
42
|
+
)
|
|
43
|
+
except ImportError:
|
|
44
|
+
# Fall back to absolute imports (when run directly)
|
|
31
45
|
from pathlib import Path
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
from
|
|
37
|
-
from
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
|
|
47
|
+
# Add parent directory to path
|
|
48
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
49
|
+
|
|
50
|
+
from event_handlers import EventHandlers
|
|
51
|
+
from memory_integration import MemoryHookManager
|
|
52
|
+
from response_tracking import ResponseTrackingManager
|
|
53
|
+
from services import (
|
|
54
|
+
ConnectionManagerService,
|
|
55
|
+
DuplicateEventDetector,
|
|
56
|
+
StateManagerService,
|
|
57
|
+
SubagentResponseProcessor,
|
|
40
58
|
)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
Debug mode configuration for hook processing.
|
|
62
|
+
|
|
63
|
+
WHY enabled by default: Hook processing can be complex and hard to debug.
|
|
64
|
+
Having debug output available by default helps diagnose issues during development.
|
|
65
|
+
Production deployments can disable via environment variable.
|
|
66
|
+
|
|
67
|
+
Performance Impact: Debug logging adds ~5-10% overhead but provides crucial
|
|
68
|
+
visibility into event flow, timing, and error conditions.
|
|
69
|
+
"""
|
|
70
|
+
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
|
|
71
|
+
|
|
72
|
+
"""
|
|
73
|
+
Conditional imports with graceful fallbacks for testing and modularity.
|
|
74
|
+
|
|
75
|
+
WHY conditional imports:
|
|
76
|
+
- Tests may not have full environment setup
|
|
77
|
+
- Allows hooks to work in minimal configurations
|
|
78
|
+
- Graceful degradation when dependencies unavailable
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
# Import get_connection_pool for backward compatibility with tests
|
|
51
82
|
try:
|
|
52
|
-
import
|
|
53
|
-
SOCKETIO_AVAILABLE = True
|
|
83
|
+
from claude_mpm.core.socketio_pool import get_connection_pool
|
|
54
84
|
except ImportError:
|
|
55
|
-
|
|
56
|
-
|
|
85
|
+
get_connection_pool = None
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
Global singleton pattern for hook handler.
|
|
89
|
+
|
|
90
|
+
WHY singleton:
|
|
91
|
+
- Only one handler should process Claude Code events
|
|
92
|
+
- Maintains consistent state across all hook invocations
|
|
93
|
+
- Prevents duplicate event processing
|
|
94
|
+
- Thread-safe initialization with lock
|
|
95
|
+
|
|
96
|
+
GOTCHA: Must use get_global_handler() not direct access to avoid race conditions.
|
|
97
|
+
"""
|
|
98
|
+
_global_handler = None
|
|
99
|
+
_handler_lock = threading.Lock()
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
Version compatibility checking.
|
|
103
|
+
|
|
104
|
+
WHY version checking:
|
|
105
|
+
- Claude Code hook support was added in v1.0.92
|
|
106
|
+
- Earlier versions don't support matcher-based configuration
|
|
107
|
+
- Prevents confusing errors with unsupported versions
|
|
108
|
+
|
|
109
|
+
Security: Version checking prevents execution on incompatible environments.
|
|
110
|
+
"""
|
|
111
|
+
MIN_CLAUDE_VERSION = "1.0.92"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def check_claude_version() -> Tuple[bool, Optional[str]]:
|
|
115
|
+
"""
|
|
116
|
+
Verify Claude Code version compatibility for hook support.
|
|
117
|
+
|
|
118
|
+
Executes 'claude --version' command to detect installed version and
|
|
119
|
+
compares against minimum required version for hook functionality.
|
|
120
|
+
|
|
121
|
+
Version Checking Logic:
|
|
122
|
+
1. Execute 'claude --version' with timeout
|
|
123
|
+
2. Parse version string using regex
|
|
124
|
+
3. Compare against MIN_CLAUDE_VERSION (1.0.92)
|
|
125
|
+
4. Return compatibility status and detected version
|
|
126
|
+
|
|
127
|
+
WHY this check is critical:
|
|
128
|
+
- Hook support was added in Claude Code v1.0.92
|
|
129
|
+
- Earlier versions don't understand matcher-based hooks
|
|
130
|
+
- Prevents cryptic errors from unsupported configurations
|
|
131
|
+
- Allows graceful fallback or user notification
|
|
132
|
+
|
|
133
|
+
Error Handling:
|
|
134
|
+
- Command timeout after 5 seconds
|
|
135
|
+
- Subprocess errors caught and logged
|
|
136
|
+
- Invalid version formats handled gracefully
|
|
137
|
+
- Returns (False, None) on any failure
|
|
138
|
+
|
|
139
|
+
Performance Notes:
|
|
140
|
+
- Subprocess call has ~100ms overhead
|
|
141
|
+
- Result should be cached by caller
|
|
142
|
+
- Only called during initialization
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Tuple[bool, Optional[str]]:
|
|
146
|
+
- bool: True if version is compatible
|
|
147
|
+
- str|None: Detected version string, None if detection failed
|
|
148
|
+
|
|
149
|
+
Examples:
|
|
150
|
+
>>> is_compatible, version = check_claude_version()
|
|
151
|
+
>>> if not is_compatible:
|
|
152
|
+
... print(f"Claude Code {version or 'unknown'} is not supported")
|
|
153
|
+
"""
|
|
154
|
+
try:
|
|
155
|
+
# Try to detect Claude Code version
|
|
156
|
+
result = subprocess.run(
|
|
157
|
+
["claude", "--version"],
|
|
158
|
+
capture_output=True,
|
|
159
|
+
text=True,
|
|
160
|
+
timeout=5,
|
|
161
|
+
check=False,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
if result.returncode == 0:
|
|
165
|
+
version_text = result.stdout.strip()
|
|
166
|
+
# Extract version number (e.g., "1.0.92 (Claude Code)" -> "1.0.92")
|
|
167
|
+
match = re.match(r"^([\d\.]+)", version_text)
|
|
168
|
+
if match:
|
|
169
|
+
version = match.group(1)
|
|
170
|
+
|
|
171
|
+
# Compare versions
|
|
172
|
+
def parse_version(v: str):
|
|
173
|
+
try:
|
|
174
|
+
return [int(x) for x in v.split(".")]
|
|
175
|
+
except (ValueError, AttributeError):
|
|
176
|
+
return [0]
|
|
177
|
+
|
|
178
|
+
current = parse_version(version)
|
|
179
|
+
required = parse_version(MIN_CLAUDE_VERSION)
|
|
180
|
+
|
|
181
|
+
# Check if current version meets minimum
|
|
182
|
+
for i in range(max(len(current), len(required))):
|
|
183
|
+
curr_part = current[i] if i < len(current) else 0
|
|
184
|
+
req_part = required[i] if i < len(required) else 0
|
|
185
|
+
|
|
186
|
+
if curr_part < req_part:
|
|
187
|
+
if DEBUG:
|
|
188
|
+
print(
|
|
189
|
+
f"⚠️ Claude Code {version} does not support matcher-based hooks "
|
|
190
|
+
f"(requires {MIN_CLAUDE_VERSION}+). Hook monitoring disabled.",
|
|
191
|
+
file=sys.stderr,
|
|
192
|
+
)
|
|
193
|
+
return False, version
|
|
194
|
+
if curr_part > req_part:
|
|
195
|
+
return True, version
|
|
196
|
+
|
|
197
|
+
return True, version
|
|
198
|
+
except Exception as e:
|
|
199
|
+
if DEBUG:
|
|
200
|
+
print(
|
|
201
|
+
f"Warning: Could not detect Claude Code version: {e}", file=sys.stderr
|
|
202
|
+
)
|
|
57
203
|
|
|
58
|
-
|
|
204
|
+
return False, None
|
|
59
205
|
|
|
60
206
|
|
|
61
207
|
class ClaudeHookHandler:
|
|
62
|
-
"""
|
|
63
|
-
|
|
64
|
-
WHY
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
208
|
+
"""Refactored hook handler with service-oriented architecture.
|
|
209
|
+
|
|
210
|
+
WHY service-oriented approach:
|
|
211
|
+
- Modular design with clear service boundaries
|
|
212
|
+
- Each service handles a specific responsibility
|
|
213
|
+
- Easier to test, maintain, and extend
|
|
214
|
+
- Reduced complexity in main handler class
|
|
69
215
|
"""
|
|
70
|
-
|
|
216
|
+
|
|
71
217
|
def __init__(self):
|
|
72
|
-
#
|
|
73
|
-
self.
|
|
74
|
-
self.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
#
|
|
78
|
-
self.
|
|
79
|
-
|
|
80
|
-
self.
|
|
81
|
-
|
|
82
|
-
#
|
|
218
|
+
# Initialize services
|
|
219
|
+
self.state_manager = StateManagerService()
|
|
220
|
+
self.connection_manager = ConnectionManagerService()
|
|
221
|
+
self.duplicate_detector = DuplicateEventDetector()
|
|
222
|
+
|
|
223
|
+
# Initialize extracted managers
|
|
224
|
+
self.memory_hook_manager = MemoryHookManager()
|
|
225
|
+
self.response_tracking_manager = ResponseTrackingManager()
|
|
226
|
+
self.event_handlers = EventHandlers(self)
|
|
227
|
+
|
|
228
|
+
# Initialize subagent processor with dependencies
|
|
229
|
+
self.subagent_processor = SubagentResponseProcessor(
|
|
230
|
+
self.state_manager, self.response_tracking_manager, self.connection_manager
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Backward compatibility properties for tests
|
|
234
|
+
self.connection_pool = self.connection_manager.connection_pool
|
|
235
|
+
|
|
236
|
+
# Expose state manager properties for backward compatibility
|
|
237
|
+
self.active_delegations = self.state_manager.active_delegations
|
|
238
|
+
self.delegation_history = self.state_manager.delegation_history
|
|
239
|
+
self.delegation_requests = self.state_manager.delegation_requests
|
|
240
|
+
self.pending_prompts = self.state_manager.pending_prompts
|
|
241
|
+
|
|
242
|
+
# Initialize git branch cache (used by event_handlers)
|
|
83
243
|
self._git_branch_cache = {}
|
|
84
244
|
self._git_branch_cache_time = {}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if session_id and agent_type and agent_type != 'unknown':
|
|
98
|
-
self.active_delegations[session_id] = agent_type
|
|
99
|
-
key = f"{session_id}:{datetime.now().timestamp()}"
|
|
100
|
-
self.delegation_history.append((key, agent_type))
|
|
101
|
-
|
|
102
|
-
# Clean up old delegations (older than 5 minutes)
|
|
103
|
-
cutoff_time = datetime.now().timestamp() - 300
|
|
104
|
-
keys_to_remove = []
|
|
105
|
-
for sid in list(self.active_delegations.keys()):
|
|
106
|
-
# Check if this is an old entry by looking in history
|
|
107
|
-
found_recent = False
|
|
108
|
-
for hist_key, _ in reversed(self.delegation_history):
|
|
109
|
-
if hist_key.startswith(sid):
|
|
110
|
-
_, timestamp = hist_key.split(':', 1)
|
|
111
|
-
if float(timestamp) > cutoff_time:
|
|
112
|
-
found_recent = True
|
|
113
|
-
break
|
|
114
|
-
if not found_recent:
|
|
115
|
-
keys_to_remove.append(sid)
|
|
116
|
-
|
|
117
|
-
for key in keys_to_remove:
|
|
118
|
-
del self.active_delegations[key]
|
|
119
|
-
|
|
120
|
-
def _get_delegation_agent_type(self, session_id: str) -> str:
|
|
121
|
-
"""Get the agent type for a session's active delegation."""
|
|
122
|
-
# First try exact session match
|
|
123
|
-
if session_id and session_id in self.active_delegations:
|
|
124
|
-
return self.active_delegations[session_id]
|
|
125
|
-
|
|
126
|
-
# Then try to find in recent history
|
|
127
|
-
if session_id:
|
|
128
|
-
for key, agent_type in reversed(self.delegation_history):
|
|
129
|
-
if key.startswith(session_id):
|
|
130
|
-
return agent_type
|
|
131
|
-
|
|
132
|
-
return 'unknown'
|
|
133
|
-
|
|
134
|
-
def _initialize_memory_hooks(self):
|
|
135
|
-
"""Initialize memory hooks for automatic agent memory management.
|
|
136
|
-
|
|
137
|
-
WHY: This activates the memory system by connecting Claude Code hook events
|
|
138
|
-
to our memory integration hooks. This enables automatic memory injection
|
|
139
|
-
before delegations and learning extraction after delegations.
|
|
140
|
-
|
|
141
|
-
DESIGN DECISION: We initialize hooks here in the Claude hook handler because
|
|
142
|
-
this is where Claude Code events are processed. This ensures memory hooks
|
|
143
|
-
are triggered at the right times during agent delegation.
|
|
245
|
+
|
|
246
|
+
def handle(self):
|
|
247
|
+
"""Process hook event with minimal overhead and timeout protection.
|
|
248
|
+
|
|
249
|
+
WHY this approach:
|
|
250
|
+
- Fast path processing for minimal latency (no blocking waits)
|
|
251
|
+
- Non-blocking Socket.IO connection and event emission
|
|
252
|
+
- Timeout protection prevents indefinite hangs
|
|
253
|
+
- Connection timeout prevents indefinite hangs
|
|
254
|
+
- Graceful degradation if Socket.IO unavailable
|
|
255
|
+
- Always continues regardless of event status
|
|
256
|
+
- Process exits after handling to prevent accumulation
|
|
144
257
|
"""
|
|
258
|
+
_continue_sent = False # Track if continue has been sent
|
|
259
|
+
|
|
260
|
+
def timeout_handler(signum, frame):
|
|
261
|
+
"""Handle timeout by forcing exit."""
|
|
262
|
+
nonlocal _continue_sent
|
|
263
|
+
if DEBUG:
|
|
264
|
+
print(f"Hook handler timeout (pid: {os.getpid()})", file=sys.stderr)
|
|
265
|
+
if not _continue_sent:
|
|
266
|
+
self._continue_execution()
|
|
267
|
+
_continue_sent = True
|
|
268
|
+
sys.exit(0)
|
|
269
|
+
|
|
145
270
|
try:
|
|
146
|
-
#
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
271
|
+
# Set a 10-second timeout for the entire operation
|
|
272
|
+
signal.signal(signal.SIGALRM, timeout_handler)
|
|
273
|
+
signal.alarm(10)
|
|
274
|
+
|
|
275
|
+
# Read and parse event
|
|
276
|
+
event = self._read_hook_event()
|
|
277
|
+
if not event:
|
|
278
|
+
if not _continue_sent:
|
|
279
|
+
self._continue_execution()
|
|
280
|
+
_continue_sent = True
|
|
281
|
+
return
|
|
282
|
+
|
|
283
|
+
# Check for duplicate events (same event within 100ms)
|
|
284
|
+
if self.duplicate_detector.is_duplicate(event):
|
|
151
285
|
if DEBUG:
|
|
152
|
-
print(
|
|
286
|
+
print(
|
|
287
|
+
f"[{datetime.now(timezone.utc).isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})",
|
|
288
|
+
file=sys.stderr,
|
|
289
|
+
)
|
|
290
|
+
# Still need to output continue for this invocation
|
|
291
|
+
if not _continue_sent:
|
|
292
|
+
self._continue_execution()
|
|
293
|
+
_continue_sent = True
|
|
153
294
|
return
|
|
154
|
-
|
|
155
|
-
#
|
|
156
|
-
self.pre_delegation_hook = MemoryPreDelegationHook(config)
|
|
157
|
-
|
|
158
|
-
# Initialize post-delegation hook if auto-learning is enabled
|
|
159
|
-
if config.get('memory.auto_learning', True): # Default to True now
|
|
160
|
-
self.post_delegation_hook = MemoryPostDelegationHook(config)
|
|
161
|
-
|
|
162
|
-
self.memory_hooks_initialized = True
|
|
163
|
-
|
|
164
|
-
if DEBUG:
|
|
165
|
-
hooks_info = []
|
|
166
|
-
if self.pre_delegation_hook:
|
|
167
|
-
hooks_info.append("pre-delegation")
|
|
168
|
-
if self.post_delegation_hook:
|
|
169
|
-
hooks_info.append("post-delegation")
|
|
170
|
-
print(f"✅ Memory hooks initialized: {', '.join(hooks_info)}", file=sys.stderr)
|
|
171
|
-
|
|
172
|
-
except Exception as e:
|
|
295
|
+
|
|
296
|
+
# Debug: Log that we're processing an event
|
|
173
297
|
if DEBUG:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
298
|
+
hook_type = event.get("hook_event_name", "unknown")
|
|
299
|
+
print(
|
|
300
|
+
f"\n[{datetime.now(timezone.utc).isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})",
|
|
301
|
+
file=sys.stderr,
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
# Perform periodic cleanup if needed
|
|
305
|
+
if self.state_manager.increment_events_processed():
|
|
306
|
+
self.state_manager.cleanup_old_entries()
|
|
307
|
+
# Also cleanup old correlation files
|
|
308
|
+
from .correlation_manager import CorrelationManager
|
|
309
|
+
|
|
310
|
+
CorrelationManager.cleanup_old()
|
|
311
|
+
if DEBUG:
|
|
312
|
+
print(
|
|
313
|
+
f"🧹 Performed cleanup after {self.state_manager.events_processed} events",
|
|
314
|
+
file=sys.stderr,
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# Route event to appropriate handler
|
|
318
|
+
# Handlers can optionally return modified input for PreToolUse events
|
|
319
|
+
modified_input = self._route_event(event)
|
|
320
|
+
|
|
321
|
+
# Always continue execution (only if not already sent)
|
|
322
|
+
if not _continue_sent:
|
|
323
|
+
self._continue_execution(modified_input)
|
|
324
|
+
_continue_sent = True
|
|
325
|
+
|
|
326
|
+
except Exception:
|
|
327
|
+
# Fail fast and silent (only send continue if not already sent)
|
|
328
|
+
if not _continue_sent:
|
|
329
|
+
self._continue_execution()
|
|
330
|
+
_continue_sent = True
|
|
331
|
+
finally:
|
|
332
|
+
# Cancel the alarm
|
|
333
|
+
signal.alarm(0)
|
|
334
|
+
|
|
335
|
+
def _read_hook_event(self) -> dict:
|
|
185
336
|
"""
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if (cache_key in self._git_branch_cache
|
|
195
|
-
and cache_key in self._git_branch_cache_time
|
|
196
|
-
and current_time - self._git_branch_cache_time[cache_key] < 30):
|
|
197
|
-
return self._git_branch_cache[cache_key]
|
|
198
|
-
|
|
199
|
-
# Try to get git branch
|
|
200
|
-
try:
|
|
201
|
-
# Change to the working directory temporarily
|
|
202
|
-
original_cwd = os.getcwd()
|
|
203
|
-
os.chdir(working_dir)
|
|
204
|
-
|
|
205
|
-
# Run git command to get current branch
|
|
206
|
-
result = subprocess.run(
|
|
207
|
-
['git', 'branch', '--show-current'],
|
|
208
|
-
capture_output=True,
|
|
209
|
-
text=True,
|
|
210
|
-
timeout=2 # Quick timeout to avoid hanging
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
# Restore original directory
|
|
214
|
-
os.chdir(original_cwd)
|
|
215
|
-
|
|
216
|
-
if result.returncode == 0 and result.stdout.strip():
|
|
217
|
-
branch = result.stdout.strip()
|
|
218
|
-
# Cache the result
|
|
219
|
-
self._git_branch_cache[cache_key] = branch
|
|
220
|
-
self._git_branch_cache_time[cache_key] = current_time
|
|
221
|
-
return branch
|
|
222
|
-
else:
|
|
223
|
-
# Not a git repository or no branch
|
|
224
|
-
self._git_branch_cache[cache_key] = 'Unknown'
|
|
225
|
-
self._git_branch_cache_time[cache_key] = current_time
|
|
226
|
-
return 'Unknown'
|
|
227
|
-
|
|
228
|
-
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError, OSError):
|
|
229
|
-
# Git not available or command failed
|
|
230
|
-
self._git_branch_cache[cache_key] = 'Unknown'
|
|
231
|
-
self._git_branch_cache_time[cache_key] = current_time
|
|
232
|
-
return 'Unknown'
|
|
233
|
-
|
|
234
|
-
def _get_socketio_client(self):
|
|
235
|
-
"""Get or create Socket.IO client.
|
|
236
|
-
|
|
237
|
-
WHY this approach:
|
|
238
|
-
- Reuses existing connection when possible
|
|
239
|
-
- Creates new connection only when needed
|
|
240
|
-
- Handles connection failures gracefully
|
|
337
|
+
Read and parse hook event from stdin with timeout.
|
|
338
|
+
|
|
339
|
+
WHY: Centralized event reading with error handling and timeout
|
|
340
|
+
ensures consistent parsing and validation while preventing
|
|
341
|
+
processes from hanging indefinitely on stdin.read().
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
Parsed event dictionary or None if invalid/timeout
|
|
241
345
|
"""
|
|
242
|
-
if not SOCKETIO_AVAILABLE:
|
|
243
|
-
return None
|
|
244
|
-
|
|
245
|
-
# Check if we have a connected client
|
|
246
|
-
if self.sio_client and self.sio_connected:
|
|
247
|
-
try:
|
|
248
|
-
# Test if still connected
|
|
249
|
-
if self.sio_client.connected:
|
|
250
|
-
return self.sio_client
|
|
251
|
-
except:
|
|
252
|
-
pass
|
|
253
|
-
|
|
254
|
-
# Need to create new client
|
|
255
346
|
try:
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
)
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
347
|
+
# Check if data is available on stdin with 1 second timeout
|
|
348
|
+
if sys.stdin.isatty():
|
|
349
|
+
# Interactive terminal - no data expected
|
|
350
|
+
return None
|
|
351
|
+
|
|
352
|
+
ready, _, _ = select.select([sys.stdin], [], [], 1.0)
|
|
353
|
+
if not ready:
|
|
354
|
+
# No data available within timeout
|
|
355
|
+
if DEBUG:
|
|
356
|
+
print("No hook event data received within timeout", file=sys.stderr)
|
|
357
|
+
return None
|
|
358
|
+
|
|
359
|
+
# Data is available, read it
|
|
360
|
+
event_data = sys.stdin.read()
|
|
361
|
+
if not event_data.strip():
|
|
362
|
+
# Empty or whitespace-only data
|
|
363
|
+
return None
|
|
364
|
+
|
|
365
|
+
parsed = json.loads(event_data)
|
|
366
|
+
# Debug: Log the actual event format we receive
|
|
267
367
|
if DEBUG:
|
|
268
|
-
print(
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
368
|
+
print(
|
|
369
|
+
f"Received event with keys: {list(parsed.keys())}", file=sys.stderr
|
|
370
|
+
)
|
|
371
|
+
for key in ["hook_event_name", "event", "type", "event_type"]:
|
|
372
|
+
if key in parsed:
|
|
373
|
+
print(f" {key} = '{parsed[key]}'", file=sys.stderr)
|
|
374
|
+
return parsed
|
|
375
|
+
except (json.JSONDecodeError, ValueError) as e:
|
|
376
|
+
if DEBUG:
|
|
377
|
+
print(f"Failed to parse hook event: {e}", file=sys.stderr)
|
|
378
|
+
return None
|
|
272
379
|
except Exception as e:
|
|
273
380
|
if DEBUG:
|
|
274
|
-
print(f"
|
|
275
|
-
self.sio_client = None
|
|
276
|
-
self.sio_connected = False
|
|
381
|
+
print(f"Error reading hook event: {e}", file=sys.stderr)
|
|
277
382
|
return None
|
|
278
|
-
|
|
279
|
-
def
|
|
280
|
-
"""Process hook event with minimal overhead and zero blocking delays.
|
|
281
|
-
|
|
282
|
-
WHY this approach:
|
|
283
|
-
- Fast path processing for minimal latency (no blocking waits)
|
|
284
|
-
- Non-blocking Socket.IO connection and event emission
|
|
285
|
-
- Removed sleep() delays that were adding 100ms+ to every hook
|
|
286
|
-
- Connection timeout prevents indefinite hangs
|
|
287
|
-
- Graceful degradation if Socket.IO unavailable
|
|
288
|
-
- Always continues regardless of event status
|
|
383
|
+
|
|
384
|
+
def _route_event(self, event: dict) -> Optional[dict]:
|
|
289
385
|
"""
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
self._handle_pre_tool_fast(event)
|
|
301
|
-
elif hook_type == 'PostToolUse':
|
|
302
|
-
self._handle_post_tool_fast(event)
|
|
303
|
-
elif hook_type == 'Notification':
|
|
304
|
-
self._handle_notification_fast(event)
|
|
305
|
-
elif hook_type == 'Stop':
|
|
306
|
-
self._handle_stop_fast(event)
|
|
307
|
-
elif hook_type == 'SubagentStop':
|
|
308
|
-
self._handle_subagent_stop_fast(event)
|
|
309
|
-
|
|
310
|
-
# Socket.IO emit is non-blocking and will complete asynchronously
|
|
311
|
-
# Removed sleep() to eliminate 100ms delay that was blocking Claude execution
|
|
312
|
-
|
|
313
|
-
# Always continue
|
|
314
|
-
print(json.dumps({"action": "continue"}))
|
|
315
|
-
|
|
316
|
-
except:
|
|
317
|
-
# Fail fast and silent
|
|
318
|
-
print(json.dumps({"action": "continue"}))
|
|
319
|
-
|
|
320
|
-
def _emit_socketio_event(self, namespace: str, event: str, data: dict):
|
|
321
|
-
"""Emit Socket.IO event using direct client.
|
|
322
|
-
|
|
323
|
-
WHY direct client approach:
|
|
324
|
-
- Simple synchronous emission
|
|
325
|
-
- No threading complexity
|
|
326
|
-
- Reliable delivery
|
|
327
|
-
- Fast when connection is reused
|
|
386
|
+
Route event to appropriate handler based on type.
|
|
387
|
+
|
|
388
|
+
WHY: Centralized routing reduces complexity and makes
|
|
389
|
+
it easier to add new event types.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
event: Hook event dictionary
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
Modified input for PreToolUse events (v2.0.30+), None otherwise
|
|
328
396
|
"""
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
397
|
+
import time
|
|
398
|
+
|
|
399
|
+
# Try multiple field names for compatibility
|
|
400
|
+
hook_type = (
|
|
401
|
+
event.get("hook_event_name")
|
|
402
|
+
or event.get("event")
|
|
403
|
+
or event.get("type")
|
|
404
|
+
or event.get("event_type")
|
|
405
|
+
or event.get("hook_event_type")
|
|
406
|
+
or "unknown"
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
# Log the actual event structure for debugging
|
|
410
|
+
if DEBUG and hook_type == "unknown":
|
|
411
|
+
print(f"Unknown event format, keys: {list(event.keys())}", file=sys.stderr)
|
|
412
|
+
print(f"Event sample: {str(event)[:200]}", file=sys.stderr)
|
|
413
|
+
|
|
414
|
+
# Map event types to handlers
|
|
415
|
+
event_handlers = {
|
|
416
|
+
"UserPromptSubmit": self.event_handlers.handle_user_prompt_fast,
|
|
417
|
+
"PreToolUse": self.event_handlers.handle_pre_tool_fast,
|
|
418
|
+
"PostToolUse": self.event_handlers.handle_post_tool_fast,
|
|
419
|
+
"Notification": self.event_handlers.handle_notification_fast,
|
|
420
|
+
"Stop": self.event_handlers.handle_stop_fast,
|
|
421
|
+
"SubagentStop": self.event_handlers.handle_subagent_stop_fast,
|
|
422
|
+
"SubagentStart": self.event_handlers.handle_session_start_fast,
|
|
423
|
+
"SessionStart": self.event_handlers.handle_session_start_fast,
|
|
424
|
+
"AssistantResponse": self.event_handlers.handle_assistant_response,
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
# Call appropriate handler if exists
|
|
428
|
+
handler = event_handlers.get(hook_type)
|
|
429
|
+
if handler:
|
|
430
|
+
# Track execution timing for hook emission
|
|
431
|
+
start_time = time.time()
|
|
432
|
+
success = False
|
|
433
|
+
error_message = None
|
|
434
|
+
result = None
|
|
435
|
+
|
|
336
436
|
try:
|
|
337
|
-
#
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
client.emit('claude_event', claude_event_data)
|
|
346
|
-
|
|
347
|
-
if DEBUG:
|
|
348
|
-
print(f"Emitted Socket.IO event: hook.{event}", file=sys.stderr)
|
|
349
|
-
|
|
437
|
+
# Handlers can optionally return modified input
|
|
438
|
+
result = handler(event)
|
|
439
|
+
success = True
|
|
440
|
+
# Only PreToolUse handlers should return modified input
|
|
441
|
+
if hook_type == "PreToolUse" and result is not None:
|
|
442
|
+
return_value = result
|
|
443
|
+
else:
|
|
444
|
+
return_value = None
|
|
350
445
|
except Exception as e:
|
|
446
|
+
error_message = str(e)
|
|
447
|
+
return_value = None
|
|
351
448
|
if DEBUG:
|
|
352
|
-
print(f"
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
return
|
|
369
|
-
|
|
370
|
-
# Get working directory and git branch
|
|
371
|
-
working_dir = event.get('cwd', '')
|
|
372
|
-
git_branch = self._get_git_branch(working_dir) if working_dir else 'Unknown'
|
|
373
|
-
|
|
374
|
-
# Extract comprehensive prompt data
|
|
375
|
-
prompt_data = {
|
|
376
|
-
'event_type': 'user_prompt',
|
|
377
|
-
'prompt_text': prompt,
|
|
378
|
-
'prompt_preview': prompt[:200] if len(prompt) > 200 else prompt,
|
|
379
|
-
'prompt_length': len(prompt),
|
|
380
|
-
'session_id': event.get('session_id', ''),
|
|
381
|
-
'working_directory': working_dir,
|
|
382
|
-
'git_branch': git_branch,
|
|
383
|
-
'timestamp': datetime.now().isoformat(),
|
|
384
|
-
'is_command': prompt.startswith('/'),
|
|
385
|
-
'contains_code': '```' in prompt or 'python' in prompt.lower() or 'javascript' in prompt.lower(),
|
|
386
|
-
'urgency': 'high' if any(word in prompt.lower() for word in ['urgent', 'error', 'bug', 'fix', 'broken']) else 'normal'
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
# Emit to /hook namespace
|
|
390
|
-
self._emit_socketio_event('/hook', 'user_prompt', prompt_data)
|
|
391
|
-
|
|
392
|
-
def _handle_pre_tool_fast(self, event):
|
|
393
|
-
"""Handle pre-tool use with comprehensive data capture.
|
|
394
|
-
|
|
395
|
-
WHY comprehensive capture:
|
|
396
|
-
- Captures tool parameters for debugging and security analysis
|
|
397
|
-
- Provides context about what Claude is about to do
|
|
398
|
-
- Enables pattern analysis and security monitoring
|
|
399
|
-
"""
|
|
400
|
-
tool_name = event.get('tool_name', '')
|
|
401
|
-
tool_input = event.get('tool_input', {})
|
|
402
|
-
|
|
403
|
-
# Extract key parameters based on tool type
|
|
404
|
-
tool_params = self._extract_tool_parameters(tool_name, tool_input)
|
|
405
|
-
|
|
406
|
-
# Classify tool operation
|
|
407
|
-
operation_type = self._classify_tool_operation(tool_name, tool_input)
|
|
408
|
-
|
|
409
|
-
# Get working directory and git branch
|
|
410
|
-
working_dir = event.get('cwd', '')
|
|
411
|
-
git_branch = self._get_git_branch(working_dir) if working_dir else 'Unknown'
|
|
412
|
-
|
|
413
|
-
pre_tool_data = {
|
|
414
|
-
'event_type': 'pre_tool',
|
|
415
|
-
'tool_name': tool_name,
|
|
416
|
-
'operation_type': operation_type,
|
|
417
|
-
'tool_parameters': tool_params,
|
|
418
|
-
'session_id': event.get('session_id', ''),
|
|
419
|
-
'working_directory': working_dir,
|
|
420
|
-
'git_branch': git_branch,
|
|
421
|
-
'timestamp': datetime.now().isoformat(),
|
|
422
|
-
'parameter_count': len(tool_input) if isinstance(tool_input, dict) else 0,
|
|
423
|
-
'is_file_operation': tool_name in ['Write', 'Edit', 'MultiEdit', 'Read', 'LS', 'Glob'],
|
|
424
|
-
'is_execution': tool_name in ['Bash', 'NotebookEdit'],
|
|
425
|
-
'is_delegation': tool_name == 'Task',
|
|
426
|
-
'security_risk': self._assess_security_risk(tool_name, tool_input)
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
# Add delegation-specific data if this is a Task tool
|
|
430
|
-
if tool_name == 'Task' and isinstance(tool_input, dict):
|
|
431
|
-
agent_type = tool_input.get('subagent_type', 'unknown')
|
|
432
|
-
pre_tool_data['delegation_details'] = {
|
|
433
|
-
'agent_type': agent_type,
|
|
434
|
-
'prompt': tool_input.get('prompt', ''),
|
|
435
|
-
'description': tool_input.get('description', ''),
|
|
436
|
-
'task_preview': (tool_input.get('prompt', '') or tool_input.get('description', ''))[:100]
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
# Track this delegation for SubagentStop correlation
|
|
440
|
-
session_id = event.get('session_id', '')
|
|
441
|
-
if session_id and agent_type != 'unknown':
|
|
442
|
-
self._track_delegation(session_id, agent_type)
|
|
443
|
-
|
|
444
|
-
# Trigger memory pre-delegation hook
|
|
445
|
-
self._trigger_memory_pre_delegation_hook(agent_type, tool_input, session_id)
|
|
446
|
-
|
|
447
|
-
self._emit_socketio_event('/hook', 'pre_tool', pre_tool_data)
|
|
448
|
-
|
|
449
|
-
def _handle_post_tool_fast(self, event):
|
|
450
|
-
"""Handle post-tool use with comprehensive data capture.
|
|
451
|
-
|
|
452
|
-
WHY comprehensive capture:
|
|
453
|
-
- Captures execution results and success/failure status
|
|
454
|
-
- Provides duration and performance metrics
|
|
455
|
-
- Enables pattern analysis of tool usage and success rates
|
|
456
|
-
"""
|
|
457
|
-
tool_name = event.get('tool_name', '')
|
|
458
|
-
exit_code = event.get('exit_code', 0)
|
|
459
|
-
|
|
460
|
-
# Extract result data
|
|
461
|
-
result_data = self._extract_tool_results(event)
|
|
462
|
-
|
|
463
|
-
# Calculate duration if timestamps are available
|
|
464
|
-
duration = self._calculate_duration(event)
|
|
465
|
-
|
|
466
|
-
# Get working directory and git branch
|
|
467
|
-
working_dir = event.get('cwd', '')
|
|
468
|
-
git_branch = self._get_git_branch(working_dir) if working_dir else 'Unknown'
|
|
469
|
-
|
|
470
|
-
post_tool_data = {
|
|
471
|
-
'event_type': 'post_tool',
|
|
472
|
-
'tool_name': tool_name,
|
|
473
|
-
'exit_code': exit_code,
|
|
474
|
-
'success': exit_code == 0,
|
|
475
|
-
'status': 'success' if exit_code == 0 else 'blocked' if exit_code == 2 else 'error',
|
|
476
|
-
'duration_ms': duration,
|
|
477
|
-
'result_summary': result_data,
|
|
478
|
-
'session_id': event.get('session_id', ''),
|
|
479
|
-
'working_directory': working_dir,
|
|
480
|
-
'git_branch': git_branch,
|
|
481
|
-
'timestamp': datetime.now().isoformat(),
|
|
482
|
-
'has_output': bool(result_data.get('output')),
|
|
483
|
-
'has_error': bool(result_data.get('error')),
|
|
484
|
-
'output_size': len(str(result_data.get('output', ''))) if result_data.get('output') else 0
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
# Handle Task delegation completion for memory hooks
|
|
488
|
-
if tool_name == 'Task':
|
|
489
|
-
session_id = event.get('session_id', '')
|
|
490
|
-
agent_type = self._get_delegation_agent_type(session_id)
|
|
491
|
-
self._trigger_memory_post_delegation_hook(agent_type, event, session_id)
|
|
492
|
-
|
|
493
|
-
self._emit_socketio_event('/hook', 'post_tool', post_tool_data)
|
|
494
|
-
|
|
495
|
-
def _extract_tool_parameters(self, tool_name: str, tool_input: dict) -> dict:
|
|
496
|
-
"""Extract relevant parameters based on tool type.
|
|
497
|
-
|
|
498
|
-
WHY tool-specific extraction:
|
|
499
|
-
- Different tools have different important parameters
|
|
500
|
-
- Provides meaningful context for dashboard display
|
|
501
|
-
- Enables tool-specific analysis and monitoring
|
|
502
|
-
"""
|
|
503
|
-
if not isinstance(tool_input, dict):
|
|
504
|
-
return {'raw_input': str(tool_input)}
|
|
505
|
-
|
|
506
|
-
# Common parameters across all tools
|
|
507
|
-
params = {
|
|
508
|
-
'input_type': type(tool_input).__name__,
|
|
509
|
-
'param_keys': list(tool_input.keys()) if tool_input else []
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
# Tool-specific parameter extraction
|
|
513
|
-
if tool_name in ['Write', 'Edit', 'MultiEdit', 'Read', 'NotebookRead', 'NotebookEdit']:
|
|
514
|
-
params.update({
|
|
515
|
-
'file_path': tool_input.get('file_path') or tool_input.get('notebook_path'),
|
|
516
|
-
'content_length': len(str(tool_input.get('content', tool_input.get('new_string', '')))),
|
|
517
|
-
'is_create': tool_name == 'Write',
|
|
518
|
-
'is_edit': tool_name in ['Edit', 'MultiEdit', 'NotebookEdit']
|
|
519
|
-
})
|
|
520
|
-
elif tool_name == 'Bash':
|
|
521
|
-
command = tool_input.get('command', '')
|
|
522
|
-
params.update({
|
|
523
|
-
'command': command[:100], # Truncate long commands
|
|
524
|
-
'command_length': len(command),
|
|
525
|
-
'has_pipe': '|' in command,
|
|
526
|
-
'has_redirect': '>' in command or '<' in command,
|
|
527
|
-
'timeout': tool_input.get('timeout')
|
|
528
|
-
})
|
|
529
|
-
elif tool_name in ['Grep', 'Glob']:
|
|
530
|
-
params.update({
|
|
531
|
-
'pattern': tool_input.get('pattern', ''),
|
|
532
|
-
'path': tool_input.get('path', ''),
|
|
533
|
-
'output_mode': tool_input.get('output_mode')
|
|
534
|
-
})
|
|
535
|
-
elif tool_name == 'WebFetch':
|
|
536
|
-
params.update({
|
|
537
|
-
'url': tool_input.get('url', ''),
|
|
538
|
-
'prompt': tool_input.get('prompt', '')[:50] # Truncate prompt
|
|
539
|
-
})
|
|
540
|
-
elif tool_name == 'Task':
|
|
541
|
-
# Special handling for Task tool (agent delegations)
|
|
542
|
-
params.update({
|
|
543
|
-
'subagent_type': tool_input.get('subagent_type', 'unknown'),
|
|
544
|
-
'description': tool_input.get('description', ''),
|
|
545
|
-
'prompt': tool_input.get('prompt', ''),
|
|
546
|
-
'prompt_preview': tool_input.get('prompt', '')[:200] if tool_input.get('prompt') else '',
|
|
547
|
-
'is_pm_delegation': tool_input.get('subagent_type') == 'pm',
|
|
548
|
-
'is_research_delegation': tool_input.get('subagent_type') == 'research',
|
|
549
|
-
'is_engineer_delegation': tool_input.get('subagent_type') == 'engineer'
|
|
550
|
-
})
|
|
551
|
-
elif tool_name == 'TodoWrite':
|
|
552
|
-
# Special handling for TodoWrite tool (task management)
|
|
553
|
-
todos = tool_input.get('todos', [])
|
|
554
|
-
params.update({
|
|
555
|
-
'todo_count': len(todos),
|
|
556
|
-
'todos': todos, # Full todo list
|
|
557
|
-
'todo_summary': self._summarize_todos(todos),
|
|
558
|
-
'has_in_progress': any(t.get('status') == 'in_progress' for t in todos),
|
|
559
|
-
'has_pending': any(t.get('status') == 'pending' for t in todos),
|
|
560
|
-
'has_completed': any(t.get('status') == 'completed' for t in todos),
|
|
561
|
-
'priorities': list(set(t.get('priority', 'medium') for t in todos))
|
|
562
|
-
})
|
|
563
|
-
|
|
564
|
-
return params
|
|
565
|
-
|
|
566
|
-
def _summarize_todos(self, todos: list) -> dict:
|
|
567
|
-
"""Create a summary of the todo list for quick understanding."""
|
|
568
|
-
if not todos:
|
|
569
|
-
return {'total': 0, 'summary': 'Empty todo list'}
|
|
570
|
-
|
|
571
|
-
status_counts = {'pending': 0, 'in_progress': 0, 'completed': 0}
|
|
572
|
-
priority_counts = {'high': 0, 'medium': 0, 'low': 0}
|
|
573
|
-
|
|
574
|
-
for todo in todos:
|
|
575
|
-
status = todo.get('status', 'pending')
|
|
576
|
-
priority = todo.get('priority', 'medium')
|
|
577
|
-
|
|
578
|
-
if status in status_counts:
|
|
579
|
-
status_counts[status] += 1
|
|
580
|
-
if priority in priority_counts:
|
|
581
|
-
priority_counts[priority] += 1
|
|
582
|
-
|
|
583
|
-
# Create a text summary
|
|
584
|
-
summary_parts = []
|
|
585
|
-
if status_counts['completed'] > 0:
|
|
586
|
-
summary_parts.append(f"{status_counts['completed']} completed")
|
|
587
|
-
if status_counts['in_progress'] > 0:
|
|
588
|
-
summary_parts.append(f"{status_counts['in_progress']} in progress")
|
|
589
|
-
if status_counts['pending'] > 0:
|
|
590
|
-
summary_parts.append(f"{status_counts['pending']} pending")
|
|
591
|
-
|
|
592
|
-
return {
|
|
593
|
-
'total': len(todos),
|
|
594
|
-
'status_counts': status_counts,
|
|
595
|
-
'priority_counts': priority_counts,
|
|
596
|
-
'summary': ', '.join(summary_parts) if summary_parts else 'No tasks'
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
def _classify_tool_operation(self, tool_name: str, tool_input: dict) -> str:
|
|
600
|
-
"""Classify the type of operation being performed."""
|
|
601
|
-
if tool_name in ['Read', 'LS', 'Glob', 'Grep', 'NotebookRead']:
|
|
602
|
-
return 'read'
|
|
603
|
-
elif tool_name in ['Write', 'Edit', 'MultiEdit', 'NotebookEdit']:
|
|
604
|
-
return 'write'
|
|
605
|
-
elif tool_name == 'Bash':
|
|
606
|
-
return 'execute'
|
|
607
|
-
elif tool_name in ['WebFetch', 'WebSearch']:
|
|
608
|
-
return 'network'
|
|
609
|
-
elif tool_name == 'TodoWrite':
|
|
610
|
-
return 'task_management'
|
|
611
|
-
elif tool_name == 'Task':
|
|
612
|
-
return 'delegation'
|
|
613
|
-
else:
|
|
614
|
-
return 'other'
|
|
615
|
-
|
|
616
|
-
def _assess_security_risk(self, tool_name: str, tool_input: dict) -> str:
|
|
617
|
-
"""Assess the security risk level of the tool operation."""
|
|
618
|
-
if tool_name == 'Bash':
|
|
619
|
-
command = tool_input.get('command', '').lower()
|
|
620
|
-
# Check for potentially dangerous commands
|
|
621
|
-
dangerous_patterns = ['rm -rf', 'sudo', 'chmod 777', 'curl', 'wget', '> /etc/', 'dd if=']
|
|
622
|
-
if any(pattern in command for pattern in dangerous_patterns):
|
|
623
|
-
return 'high'
|
|
624
|
-
elif any(word in command for word in ['install', 'delete', 'format', 'kill']):
|
|
625
|
-
return 'medium'
|
|
626
|
-
else:
|
|
627
|
-
return 'low'
|
|
628
|
-
elif tool_name in ['Write', 'Edit', 'MultiEdit']:
|
|
629
|
-
file_path = tool_input.get('file_path', '')
|
|
630
|
-
# Check for system file modifications
|
|
631
|
-
if any(path in file_path for path in ['/etc/', '/usr/', '/var/', '/sys/']):
|
|
632
|
-
return 'high'
|
|
633
|
-
elif file_path.startswith('/'):
|
|
634
|
-
return 'medium'
|
|
635
|
-
else:
|
|
636
|
-
return 'low'
|
|
637
|
-
else:
|
|
638
|
-
return 'low'
|
|
639
|
-
|
|
640
|
-
def _extract_tool_results(self, event: dict) -> dict:
|
|
641
|
-
"""Extract and summarize tool execution results."""
|
|
642
|
-
result = {
|
|
643
|
-
'exit_code': event.get('exit_code', 0),
|
|
644
|
-
'has_output': False,
|
|
645
|
-
'has_error': False
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
# Extract output if available
|
|
649
|
-
if 'output' in event:
|
|
650
|
-
output = str(event['output'])
|
|
651
|
-
result.update({
|
|
652
|
-
'has_output': bool(output.strip()),
|
|
653
|
-
'output_preview': output[:200] if len(output) > 200 else output,
|
|
654
|
-
'output_lines': len(output.split('\n')) if output else 0
|
|
655
|
-
})
|
|
656
|
-
|
|
657
|
-
# Extract error information
|
|
658
|
-
if 'error' in event or event.get('exit_code', 0) != 0:
|
|
659
|
-
error = str(event.get('error', ''))
|
|
660
|
-
result.update({
|
|
661
|
-
'has_error': True,
|
|
662
|
-
'error_preview': error[:200] if len(error) > 200 else error
|
|
663
|
-
})
|
|
664
|
-
|
|
665
|
-
return result
|
|
666
|
-
|
|
667
|
-
def _calculate_duration(self, event: dict) -> int:
|
|
668
|
-
"""Calculate operation duration in milliseconds if timestamps are available."""
|
|
669
|
-
# This would require start/end timestamps from Claude Code
|
|
670
|
-
# For now, return None as we don't have this data
|
|
449
|
+
print(f"Error handling {hook_type}: {e}", file=sys.stderr)
|
|
450
|
+
finally:
|
|
451
|
+
# Calculate duration
|
|
452
|
+
duration_ms = int((time.time() - start_time) * 1000)
|
|
453
|
+
|
|
454
|
+
# Emit hook execution event
|
|
455
|
+
self._emit_hook_execution_event(
|
|
456
|
+
hook_type=hook_type,
|
|
457
|
+
event=event,
|
|
458
|
+
success=success,
|
|
459
|
+
duration_ms=duration_ms,
|
|
460
|
+
error_message=error_message,
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
return return_value
|
|
464
|
+
|
|
671
465
|
return None
|
|
672
|
-
|
|
673
|
-
def
|
|
674
|
-
"""
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
- Captures notification type, content, and context for monitoring
|
|
679
|
-
- Enables pattern analysis of Claude's notification behavior
|
|
680
|
-
- Useful for debugging communication issues and user experience
|
|
466
|
+
|
|
467
|
+
def handle_subagent_stop(self, event: dict):
|
|
468
|
+
"""Delegate subagent stop processing to the specialized processor."""
|
|
469
|
+
self.subagent_processor.process_subagent_stop(event)
|
|
470
|
+
|
|
471
|
+
def _continue_execution(self, modified_input: Optional[dict] = None) -> None:
|
|
681
472
|
"""
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
notification_data = {
|
|
690
|
-
'event_type': 'notification',
|
|
691
|
-
'notification_type': notification_type,
|
|
692
|
-
'message': message,
|
|
693
|
-
'message_preview': message[:200] if len(message) > 200 else message,
|
|
694
|
-
'message_length': len(message),
|
|
695
|
-
'session_id': event.get('session_id', ''),
|
|
696
|
-
'working_directory': working_dir,
|
|
697
|
-
'git_branch': git_branch,
|
|
698
|
-
'timestamp': datetime.now().isoformat(),
|
|
699
|
-
'is_user_input_request': 'input' in message.lower() or 'waiting' in message.lower(),
|
|
700
|
-
'is_error_notification': 'error' in message.lower() or 'failed' in message.lower(),
|
|
701
|
-
'is_status_update': any(word in message.lower() for word in ['processing', 'analyzing', 'working', 'thinking'])
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
# Emit to /hook namespace
|
|
705
|
-
self._emit_socketio_event('/hook', 'notification', notification_data)
|
|
706
|
-
|
|
707
|
-
def _handle_stop_fast(self, event):
|
|
708
|
-
"""Handle stop events when Claude processing stops.
|
|
709
|
-
|
|
710
|
-
WHY comprehensive stop capture:
|
|
711
|
-
- Provides visibility into Claude's session lifecycle
|
|
712
|
-
- Captures stop reason and context for analysis
|
|
713
|
-
- Enables tracking of session completion patterns
|
|
714
|
-
- Useful for understanding when and why Claude stops responding
|
|
473
|
+
Send continue action to Claude with optional input modification.
|
|
474
|
+
|
|
475
|
+
WHY: Centralized response ensures consistent format
|
|
476
|
+
and makes it easier to add response modifications.
|
|
477
|
+
|
|
478
|
+
Args:
|
|
479
|
+
modified_input: Modified tool parameters for PreToolUse hooks (v2.0.30+)
|
|
715
480
|
"""
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
481
|
+
if modified_input is not None:
|
|
482
|
+
# Claude Code v2.0.30+ supports modifying PreToolUse tool inputs
|
|
483
|
+
print(json.dumps({"action": "continue", "tool_input": modified_input}))
|
|
484
|
+
else:
|
|
485
|
+
print(json.dumps({"action": "continue"}))
|
|
486
|
+
|
|
487
|
+
# Delegation methods for compatibility with event_handlers
|
|
488
|
+
def _track_delegation(self, session_id: str, agent_type: str, request_data=None):
|
|
489
|
+
"""Track delegation through state manager."""
|
|
490
|
+
self.state_manager.track_delegation(session_id, agent_type, request_data)
|
|
491
|
+
|
|
492
|
+
def _get_delegation_agent_type(self, session_id: str) -> str:
|
|
493
|
+
"""Get delegation agent type through state manager."""
|
|
494
|
+
return self.state_manager.get_delegation_agent_type(session_id)
|
|
495
|
+
|
|
496
|
+
def _get_git_branch(self, working_dir=None) -> str:
|
|
497
|
+
"""Get git branch through state manager."""
|
|
498
|
+
return self.state_manager.get_git_branch(working_dir)
|
|
499
|
+
|
|
500
|
+
def _emit_socketio_event(self, namespace: str, event: str, data: dict):
|
|
501
|
+
"""Emit event through connection manager."""
|
|
502
|
+
self.connection_manager.emit_event(namespace, event, data)
|
|
503
|
+
|
|
504
|
+
def _get_event_key(self, event: dict) -> str:
|
|
505
|
+
"""Generate event key through duplicate detector (backward compatibility)."""
|
|
506
|
+
return self.duplicate_detector.generate_event_key(event)
|
|
507
|
+
|
|
508
|
+
def _emit_hook_execution_event(
|
|
509
|
+
self,
|
|
510
|
+
hook_type: str,
|
|
511
|
+
event: dict,
|
|
512
|
+
success: bool,
|
|
513
|
+
duration_ms: int,
|
|
514
|
+
error_message: Optional[str] = None,
|
|
515
|
+
):
|
|
516
|
+
"""Emit a structured JSON event for hook execution.
|
|
517
|
+
|
|
518
|
+
This emits a normalized event following the claude_event schema to provide
|
|
519
|
+
visibility into hook processing, timing, and success/failure status.
|
|
520
|
+
|
|
521
|
+
Args:
|
|
522
|
+
hook_type: The type of hook that executed (e.g., "UserPromptSubmit", "PreToolUse")
|
|
523
|
+
event: The original hook event data
|
|
524
|
+
success: Whether the hook executed successfully
|
|
525
|
+
duration_ms: How long the hook took to execute in milliseconds
|
|
526
|
+
error_message: Optional error message if the hook failed
|
|
748
527
|
"""
|
|
749
|
-
#
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
agent_type = 'engineer'
|
|
767
|
-
elif 'pm' in task_desc or 'project' in task_desc:
|
|
768
|
-
agent_type = 'pm'
|
|
769
|
-
|
|
770
|
-
# Get working directory and git branch
|
|
771
|
-
working_dir = event.get('cwd', '')
|
|
772
|
-
git_branch = self._get_git_branch(working_dir) if working_dir else 'Unknown'
|
|
773
|
-
|
|
774
|
-
subagent_stop_data = {
|
|
775
|
-
'event_type': 'subagent_stop',
|
|
776
|
-
'agent_type': agent_type,
|
|
777
|
-
'agent_id': agent_id,
|
|
778
|
-
'reason': reason,
|
|
779
|
-
'session_id': event.get('session_id', ''),
|
|
780
|
-
'working_directory': working_dir,
|
|
781
|
-
'git_branch': git_branch,
|
|
782
|
-
'timestamp': datetime.now().isoformat(),
|
|
783
|
-
'is_successful_completion': reason in ['completed', 'finished', 'done'],
|
|
784
|
-
'is_error_termination': reason in ['error', 'timeout', 'failed', 'blocked'],
|
|
785
|
-
'is_delegation_related': agent_type in ['research', 'engineer', 'pm', 'ops'],
|
|
786
|
-
'has_results': bool(event.get('results') or event.get('output')),
|
|
787
|
-
'duration_context': event.get('duration_ms')
|
|
528
|
+
# Generate a human-readable summary based on hook type
|
|
529
|
+
summary = self._generate_hook_summary(hook_type, event, success)
|
|
530
|
+
|
|
531
|
+
# Extract common fields
|
|
532
|
+
session_id = event.get("session_id", "")
|
|
533
|
+
working_dir = event.get("cwd", "")
|
|
534
|
+
|
|
535
|
+
# Build hook execution data
|
|
536
|
+
hook_data = {
|
|
537
|
+
"hook_name": hook_type,
|
|
538
|
+
"hook_type": hook_type,
|
|
539
|
+
"session_id": session_id,
|
|
540
|
+
"working_directory": working_dir,
|
|
541
|
+
"success": success,
|
|
542
|
+
"duration_ms": duration_ms,
|
|
543
|
+
"result_summary": summary,
|
|
544
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
788
545
|
}
|
|
789
|
-
|
|
790
|
-
#
|
|
546
|
+
|
|
547
|
+
# Add error information if present
|
|
548
|
+
if error_message:
|
|
549
|
+
hook_data["error_message"] = error_message
|
|
550
|
+
|
|
551
|
+
# Add hook-specific context
|
|
552
|
+
if hook_type == "PreToolUse":
|
|
553
|
+
hook_data["tool_name"] = event.get("tool_name", "")
|
|
554
|
+
elif hook_type == "PostToolUse":
|
|
555
|
+
hook_data["tool_name"] = event.get("tool_name", "")
|
|
556
|
+
hook_data["exit_code"] = event.get("exit_code", 0)
|
|
557
|
+
elif hook_type == "UserPromptSubmit":
|
|
558
|
+
prompt = event.get("prompt", "")
|
|
559
|
+
hook_data["prompt_preview"] = prompt[:100] if len(prompt) > 100 else prompt
|
|
560
|
+
hook_data["prompt_length"] = len(prompt)
|
|
561
|
+
elif hook_type == "SubagentStop":
|
|
562
|
+
hook_data["agent_type"] = event.get("agent_type", "unknown")
|
|
563
|
+
hook_data["reason"] = event.get("reason", "unknown")
|
|
564
|
+
|
|
565
|
+
# Emit through connection manager with proper structure
|
|
566
|
+
# This uses the existing event infrastructure
|
|
567
|
+
self._emit_socketio_event("", "hook_execution", hook_data)
|
|
568
|
+
|
|
791
569
|
if DEBUG:
|
|
792
|
-
print(
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
self._emit_socketio_event('/hook', 'subagent_stop', subagent_stop_data)
|
|
796
|
-
|
|
797
|
-
def _trigger_memory_pre_delegation_hook(self, agent_type: str, tool_input: dict, session_id: str):
|
|
798
|
-
"""Trigger memory pre-delegation hook for agent memory injection.
|
|
799
|
-
|
|
800
|
-
WHY: This connects Claude Code's Task delegation events to our memory system.
|
|
801
|
-
When Claude is about to delegate to an agent, we inject the agent's memory
|
|
802
|
-
into the delegation context so the agent has access to accumulated knowledge.
|
|
803
|
-
|
|
804
|
-
DESIGN DECISION: We modify the tool_input in place to inject memory context.
|
|
805
|
-
This ensures the agent receives the memory as part of their initial context.
|
|
806
|
-
"""
|
|
807
|
-
if not self.memory_hooks_initialized or not self.pre_delegation_hook:
|
|
808
|
-
return
|
|
809
|
-
|
|
810
|
-
try:
|
|
811
|
-
# Create hook context for memory injection
|
|
812
|
-
hook_context = HookContext(
|
|
813
|
-
hook_type=HookType.PRE_DELEGATION,
|
|
814
|
-
data={
|
|
815
|
-
'agent': agent_type,
|
|
816
|
-
'context': tool_input,
|
|
817
|
-
'session_id': session_id
|
|
818
|
-
},
|
|
819
|
-
metadata={
|
|
820
|
-
'source': 'claude_hook_handler',
|
|
821
|
-
'tool_name': 'Task'
|
|
822
|
-
},
|
|
823
|
-
timestamp=datetime.now().isoformat(),
|
|
824
|
-
session_id=session_id
|
|
570
|
+
print(
|
|
571
|
+
f"📊 Hook execution event: {hook_type} - {duration_ms}ms - {'✅' if success else '❌'}",
|
|
572
|
+
file=sys.stderr,
|
|
825
573
|
)
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
# Prepend memory to the original prompt
|
|
839
|
-
enhanced_prompt = f"{memory_section}\n\n{original_prompt}"
|
|
840
|
-
tool_input['prompt'] = enhanced_prompt
|
|
841
|
-
|
|
842
|
-
if DEBUG:
|
|
843
|
-
memory_size = len(memory_section.encode('utf-8'))
|
|
844
|
-
print(f"✅ Injected {memory_size} bytes of memory for agent '{agent_type}'", file=sys.stderr)
|
|
845
|
-
|
|
846
|
-
except Exception as e:
|
|
847
|
-
if DEBUG:
|
|
848
|
-
print(f"❌ Memory pre-delegation hook failed: {e}", file=sys.stderr)
|
|
849
|
-
# Don't fail the delegation - memory is optional
|
|
850
|
-
|
|
851
|
-
def _trigger_memory_post_delegation_hook(self, agent_type: str, event: dict, session_id: str):
|
|
852
|
-
"""Trigger memory post-delegation hook for learning extraction.
|
|
853
|
-
|
|
854
|
-
WHY: This connects Claude Code's Task completion events to our memory system.
|
|
855
|
-
When an agent completes a task, we extract learnings from the result and
|
|
856
|
-
store them in the agent's memory for future use.
|
|
857
|
-
|
|
858
|
-
DESIGN DECISION: We extract learnings from both the tool output and any
|
|
859
|
-
error messages, providing comprehensive context for the memory system.
|
|
574
|
+
|
|
575
|
+
def _generate_hook_summary(self, hook_type: str, event: dict, success: bool) -> str:
|
|
576
|
+
"""Generate a human-readable summary of what the hook did.
|
|
577
|
+
|
|
578
|
+
Args:
|
|
579
|
+
hook_type: The type of hook
|
|
580
|
+
event: The hook event data
|
|
581
|
+
success: Whether the hook executed successfully
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
A brief description of what happened
|
|
860
585
|
"""
|
|
861
|
-
if not
|
|
862
|
-
return
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
)
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
learnings_extracted = result.metadata.get('learnings_extracted', 0)
|
|
905
|
-
if learnings_extracted > 0 and DEBUG:
|
|
906
|
-
print(f"✅ Extracted {learnings_extracted} learnings for agent '{agent_type}'", file=sys.stderr)
|
|
907
|
-
|
|
908
|
-
except Exception as e:
|
|
909
|
-
if DEBUG:
|
|
910
|
-
print(f"❌ Memory post-delegation hook failed: {e}", file=sys.stderr)
|
|
911
|
-
# Don't fail the delegation result - memory is optional
|
|
912
|
-
|
|
586
|
+
if not success:
|
|
587
|
+
return f"Hook {hook_type} failed during processing"
|
|
588
|
+
|
|
589
|
+
# Generate hook-specific summaries
|
|
590
|
+
if hook_type == "UserPromptSubmit":
|
|
591
|
+
prompt = event.get("prompt", "")
|
|
592
|
+
if prompt.startswith("/"):
|
|
593
|
+
return f"Processed command: {prompt.split()[0]}"
|
|
594
|
+
return f"Processed user prompt ({len(prompt)} chars)"
|
|
595
|
+
|
|
596
|
+
if hook_type == "PreToolUse":
|
|
597
|
+
tool_name = event.get("tool_name", "unknown")
|
|
598
|
+
return f"Pre-processing tool call: {tool_name}"
|
|
599
|
+
|
|
600
|
+
if hook_type == "PostToolUse":
|
|
601
|
+
tool_name = event.get("tool_name", "unknown")
|
|
602
|
+
exit_code = event.get("exit_code", 0)
|
|
603
|
+
status = "success" if exit_code == 0 else "failed"
|
|
604
|
+
return f"Completed tool call: {tool_name} ({status})"
|
|
605
|
+
|
|
606
|
+
if hook_type == "SubagentStop":
|
|
607
|
+
agent_type = event.get("agent_type", "unknown")
|
|
608
|
+
reason = event.get("reason", "unknown")
|
|
609
|
+
return f"Subagent {agent_type} stopped: {reason}"
|
|
610
|
+
|
|
611
|
+
if hook_type == "SessionStart":
|
|
612
|
+
return "New session started"
|
|
613
|
+
|
|
614
|
+
if hook_type == "Stop":
|
|
615
|
+
reason = event.get("reason", "unknown")
|
|
616
|
+
return f"Session stopped: {reason}"
|
|
617
|
+
|
|
618
|
+
if hook_type == "Notification":
|
|
619
|
+
notification_type = event.get("notification_type", "unknown")
|
|
620
|
+
return f"Notification received: {notification_type}"
|
|
621
|
+
|
|
622
|
+
if hook_type == "AssistantResponse":
|
|
623
|
+
response_len = len(event.get("response", ""))
|
|
624
|
+
return f"Assistant response generated ({response_len} chars)"
|
|
625
|
+
|
|
626
|
+
# Default summary
|
|
627
|
+
return f"Hook {hook_type} processed successfully"
|
|
628
|
+
|
|
913
629
|
def __del__(self):
|
|
914
|
-
"""Cleanup
|
|
915
|
-
if
|
|
630
|
+
"""Cleanup on handler destruction."""
|
|
631
|
+
# Clean up connection manager if it exists
|
|
632
|
+
if hasattr(self, "connection_manager") and self.connection_manager:
|
|
916
633
|
try:
|
|
917
|
-
self.
|
|
918
|
-
except:
|
|
919
|
-
pass
|
|
634
|
+
self.connection_manager.cleanup()
|
|
635
|
+
except Exception:
|
|
636
|
+
pass # Ignore cleanup errors during destruction
|
|
920
637
|
|
|
921
638
|
|
|
922
639
|
def main():
|
|
923
|
-
"""Entry point with
|
|
640
|
+
"""Entry point with singleton pattern and proper cleanup."""
|
|
641
|
+
global _global_handler
|
|
642
|
+
_continue_printed = False # Track if we've already printed continue
|
|
643
|
+
|
|
644
|
+
# Check Claude Code version compatibility first
|
|
645
|
+
is_compatible, version = check_claude_version()
|
|
646
|
+
if not is_compatible:
|
|
647
|
+
# Version incompatible - just continue without processing
|
|
648
|
+
# This prevents errors on older Claude Code versions
|
|
649
|
+
if DEBUG and version:
|
|
650
|
+
print(
|
|
651
|
+
f"Skipping hook processing due to version incompatibility ({version})",
|
|
652
|
+
file=sys.stderr,
|
|
653
|
+
)
|
|
654
|
+
print(json.dumps({"action": "continue"}))
|
|
655
|
+
sys.exit(0)
|
|
656
|
+
|
|
657
|
+
def cleanup_handler(signum=None, frame=None):
|
|
658
|
+
"""Cleanup handler for signals and exit."""
|
|
659
|
+
nonlocal _continue_printed
|
|
660
|
+
if DEBUG:
|
|
661
|
+
print(
|
|
662
|
+
f"Hook handler cleanup (pid: {os.getpid()}, signal: {signum})",
|
|
663
|
+
file=sys.stderr,
|
|
664
|
+
)
|
|
665
|
+
# Only output continue if we haven't already (i.e., if interrupted by signal)
|
|
666
|
+
if signum is not None and not _continue_printed:
|
|
667
|
+
print(json.dumps({"action": "continue"}))
|
|
668
|
+
_continue_printed = True
|
|
669
|
+
sys.exit(0)
|
|
670
|
+
|
|
671
|
+
# Register cleanup handlers
|
|
672
|
+
signal.signal(signal.SIGTERM, cleanup_handler)
|
|
673
|
+
signal.signal(signal.SIGINT, cleanup_handler)
|
|
674
|
+
# Don't register atexit handler since we're handling exit properly in main
|
|
675
|
+
|
|
924
676
|
try:
|
|
925
|
-
|
|
677
|
+
# Use singleton pattern to prevent creating multiple instances
|
|
678
|
+
with _handler_lock:
|
|
679
|
+
if _global_handler is None:
|
|
680
|
+
_global_handler = ClaudeHookHandler()
|
|
681
|
+
if DEBUG:
|
|
682
|
+
print(
|
|
683
|
+
f"✅ Created new ClaudeHookHandler singleton (pid: {os.getpid()})",
|
|
684
|
+
file=sys.stderr,
|
|
685
|
+
)
|
|
686
|
+
elif DEBUG:
|
|
687
|
+
print(
|
|
688
|
+
f"♻️ Reusing existing ClaudeHookHandler singleton (pid: {os.getpid()})",
|
|
689
|
+
file=sys.stderr,
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
handler = _global_handler
|
|
693
|
+
|
|
694
|
+
# Mark that handle() will print continue
|
|
926
695
|
handler.handle()
|
|
696
|
+
_continue_printed = True # Mark as printed since handle() always prints it
|
|
697
|
+
|
|
698
|
+
# handler.handle() already calls _continue_execution(), so we don't need to do it again
|
|
699
|
+
# Just exit cleanly
|
|
700
|
+
sys.exit(0)
|
|
701
|
+
|
|
927
702
|
except Exception as e:
|
|
928
|
-
#
|
|
929
|
-
|
|
703
|
+
# Only output continue if not already printed
|
|
704
|
+
if not _continue_printed:
|
|
705
|
+
print(json.dumps({"action": "continue"}))
|
|
706
|
+
_continue_printed = True
|
|
930
707
|
# Log error for debugging
|
|
931
708
|
if DEBUG:
|
|
932
709
|
print(f"Hook handler error: {e}", file=sys.stderr)
|
|
@@ -934,4 +711,4 @@ def main():
|
|
|
934
711
|
|
|
935
712
|
|
|
936
713
|
if __name__ == "__main__":
|
|
937
|
-
main()
|
|
714
|
+
main()
|