claude-mpm 4.13.2__py3-none-any.whl → 4.18.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +48 -17
- claude_mpm/agents/OUTPUT_STYLE.md +329 -11
- claude_mpm/agents/PM_INSTRUCTIONS.md +227 -8
- claude_mpm/agents/agent_loader.py +17 -5
- claude_mpm/agents/frontmatter_validator.py +284 -253
- claude_mpm/agents/templates/agentic-coder-optimizer.json +9 -2
- claude_mpm/agents/templates/api_qa.json +7 -1
- claude_mpm/agents/templates/clerk-ops.json +8 -1
- claude_mpm/agents/templates/code_analyzer.json +4 -1
- claude_mpm/agents/templates/dart_engineer.json +11 -1
- claude_mpm/agents/templates/data_engineer.json +11 -1
- claude_mpm/agents/templates/documentation.json +6 -1
- claude_mpm/agents/templates/engineer.json +18 -1
- claude_mpm/agents/templates/gcp_ops_agent.json +8 -1
- claude_mpm/agents/templates/golang_engineer.json +11 -1
- claude_mpm/agents/templates/java_engineer.json +12 -2
- claude_mpm/agents/templates/local_ops_agent.json +1217 -6
- claude_mpm/agents/templates/nextjs_engineer.json +11 -1
- claude_mpm/agents/templates/ops.json +8 -1
- claude_mpm/agents/templates/php-engineer.json +11 -1
- claude_mpm/agents/templates/project_organizer.json +10 -3
- claude_mpm/agents/templates/prompt-engineer.json +5 -1
- claude_mpm/agents/templates/python_engineer.json +11 -1
- claude_mpm/agents/templates/qa.json +7 -1
- claude_mpm/agents/templates/react_engineer.json +11 -1
- claude_mpm/agents/templates/refactoring_engineer.json +8 -1
- claude_mpm/agents/templates/research.json +4 -1
- claude_mpm/agents/templates/ruby-engineer.json +11 -1
- claude_mpm/agents/templates/rust_engineer.json +11 -1
- claude_mpm/agents/templates/security.json +6 -1
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/agents/templates/ticketing.json +6 -1
- claude_mpm/agents/templates/typescript_engineer.json +11 -1
- claude_mpm/agents/templates/vercel_ops_agent.json +8 -1
- claude_mpm/agents/templates/version_control.json +8 -1
- claude_mpm/agents/templates/web_qa.json +7 -1
- claude_mpm/agents/templates/web_ui.json +11 -1
- claude_mpm/cli/__init__.py +34 -706
- claude_mpm/cli/commands/agent_manager.py +25 -12
- claude_mpm/cli/commands/agent_state_manager.py +186 -0
- claude_mpm/cli/commands/agents.py +204 -148
- claude_mpm/cli/commands/aggregate.py +7 -3
- claude_mpm/cli/commands/analyze.py +9 -4
- claude_mpm/cli/commands/analyze_code.py +7 -2
- claude_mpm/cli/commands/auto_configure.py +7 -9
- claude_mpm/cli/commands/config.py +47 -13
- claude_mpm/cli/commands/configure.py +294 -1788
- claude_mpm/cli/commands/configure_agent_display.py +261 -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 +167 -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/local_deploy.py +537 -0
- claude_mpm/cli/commands/memory.py +54 -20
- claude_mpm/cli/commands/mpm_init.py +39 -25
- claude_mpm/cli/commands/mpm_init_handler.py +8 -3
- claude_mpm/cli/executor.py +202 -0
- claude_mpm/cli/helpers.py +105 -0
- claude_mpm/cli/interactive/__init__.py +3 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/parsers/__init__.py +7 -1
- claude_mpm/cli/parsers/base_parser.py +98 -3
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/shared/output_formatters.py +28 -19
- claude_mpm/cli/startup.py +481 -0
- claude_mpm/cli/utils.py +52 -1
- claude_mpm/commands/mpm-help.md +3 -0
- claude_mpm/commands/mpm-version.md +113 -0
- claude_mpm/commands/mpm.md +1 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/config/model_config.py +428 -0
- claude_mpm/core/base_service.py +13 -12
- claude_mpm/core/enums.py +452 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/instruction_reinforcement_hook.py +2 -1
- claude_mpm/core/interactive_session.py +9 -3
- claude_mpm/core/logging_config.py +6 -2
- claude_mpm/core/oneshot_session.py +8 -4
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/core/output_style_manager.py +12 -192
- claude_mpm/core/service_registry.py +5 -1
- claude_mpm/core/types.py +2 -9
- claude_mpm/core/typing_utils.py +7 -6
- claude_mpm/dashboard/static/js/dashboard.js +0 -14
- claude_mpm/dashboard/templates/index.html +3 -41
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/instruction_reinforcement.py +7 -2
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/auto_config_manager.py +10 -11
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
- claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
- claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
- claude_mpm/services/agents/registry/modification_tracker.py +5 -2
- claude_mpm/services/command_handler_service.py +11 -5
- claude_mpm/services/core/interfaces/__init__.py +74 -2
- claude_mpm/services/core/interfaces/health.py +172 -0
- claude_mpm/services/core/interfaces/model.py +281 -0
- claude_mpm/services/core/interfaces/process.py +372 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/models/__init__.py +33 -0
- claude_mpm/services/core/models/agent_config.py +12 -28
- claude_mpm/services/core/models/health.py +162 -0
- claude_mpm/services/core/models/process.py +235 -0
- claude_mpm/services/core/models/restart.py +302 -0
- claude_mpm/services/core/models/stability.py +264 -0
- claude_mpm/services/core/path_resolver.py +23 -7
- claude_mpm/services/diagnostics/__init__.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
- claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
- claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
- claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
- claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
- claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
- claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +36 -31
- claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
- claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
- claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
- claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
- claude_mpm/services/diagnostics/models.py +19 -24
- claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
- claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
- claude_mpm/services/infrastructure/monitoring/base.py +5 -13
- claude_mpm/services/infrastructure/monitoring/network.py +7 -6
- claude_mpm/services/infrastructure/monitoring/process.py +13 -12
- claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
- claude_mpm/services/infrastructure/monitoring/service.py +16 -15
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/local_ops/__init__.py +163 -0
- claude_mpm/services/local_ops/crash_detector.py +257 -0
- claude_mpm/services/local_ops/health_checks/__init__.py +28 -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 +430 -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 +9 -4
- claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
- claude_mpm/services/mcp_gateway/core/base.py +18 -31
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
- claude_mpm/services/memory_hook_service.py +4 -1
- 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 +453 -0
- claude_mpm/services/model/ollama_provider.py +415 -0
- claude_mpm/services/monitor/daemon_manager.py +3 -2
- claude_mpm/services/monitor/handlers/dashboard.py +2 -1
- claude_mpm/services/monitor/handlers/hooks.py +2 -1
- claude_mpm/services/monitor/management/lifecycle.py +3 -2
- claude_mpm/services/monitor/server.py +2 -1
- claude_mpm/services/session_management_service.py +3 -2
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/shared/async_service_base.py +16 -27
- claude_mpm/services/shared/lifecycle_service_base.py +1 -14
- claude_mpm/services/socketio/handlers/__init__.py +5 -2
- claude_mpm/services/socketio/handlers/hook.py +13 -2
- claude_mpm/services/socketio/handlers/registry.py +4 -2
- claude_mpm/services/socketio/server/main.py +10 -8
- claude_mpm/services/subprocess_launcher_service.py +14 -5
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
- claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
- claude_mpm/services/unified/deployment_strategies/local.py +6 -5
- claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
- claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
- claude_mpm/services/unified/interfaces.py +3 -1
- claude_mpm/services/unified/unified_analyzer.py +14 -10
- claude_mpm/services/unified/unified_config.py +2 -1
- claude_mpm/services/unified/unified_deployment.py +9 -4
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/skills/__init__.py +21 -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/database-migration.md +199 -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/json-data-handling.md +223 -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 +567 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -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 +310 -0
- claude_mpm/tools/code_tree_analyzer.py +177 -141
- claude_mpm/tools/code_tree_events.py +4 -2
- claude_mpm/utils/agent_dependency_loader.py +2 -2
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +117 -8
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +238 -174
- claude_mpm/dashboard/static/css/code-tree.css +0 -1639
- claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
- claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
- claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
- claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
- claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
- claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
- claude_mpm/services/project/analyzer_refactored.py +0 -450
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Refactored Claude Code hook handler with modular service architecture.
|
|
3
|
-
|
|
4
|
-
This handler uses a service-oriented architecture with:
|
|
5
|
-
- StateManagerService: Manages state and delegation tracking
|
|
6
|
-
- ConnectionManagerService: Handles SocketIO and EventBus connections
|
|
7
|
-
- SubagentResponseProcessor: Processes complex subagent responses
|
|
8
|
-
- DuplicateEventDetector: Detects and filters duplicate events
|
|
9
|
-
|
|
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
|
-
|
|
17
|
-
import json
|
|
18
|
-
import os
|
|
19
|
-
import select
|
|
20
|
-
import signal
|
|
21
|
-
import sys
|
|
22
|
-
import threading
|
|
23
|
-
from datetime import datetime, timezone
|
|
24
|
-
|
|
25
|
-
# Import extracted modules with fallback for direct execution
|
|
26
|
-
try:
|
|
27
|
-
# Try relative imports first (when imported as module)
|
|
28
|
-
from .event_handlers import EventHandlers
|
|
29
|
-
from .memory_integration import MemoryHookManager
|
|
30
|
-
from .response_tracking import ResponseTrackingManager
|
|
31
|
-
from .services import (
|
|
32
|
-
ConnectionManagerService,
|
|
33
|
-
DuplicateEventDetector,
|
|
34
|
-
StateManagerService,
|
|
35
|
-
SubagentResponseProcessor,
|
|
36
|
-
)
|
|
37
|
-
except ImportError:
|
|
38
|
-
# Fall back to absolute imports (when run directly)
|
|
39
|
-
from pathlib import Path
|
|
40
|
-
|
|
41
|
-
# Add parent directory to path
|
|
42
|
-
sys.path.insert(0, str(Path(__file__).parent))
|
|
43
|
-
|
|
44
|
-
from event_handlers import EventHandlers
|
|
45
|
-
from memory_integration import MemoryHookManager
|
|
46
|
-
from response_tracking import ResponseTrackingManager
|
|
47
|
-
from services import (
|
|
48
|
-
ConnectionManagerService,
|
|
49
|
-
DuplicateEventDetector,
|
|
50
|
-
StateManagerService,
|
|
51
|
-
SubagentResponseProcessor,
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
# Debug mode is enabled by default for better visibility into hook processing
|
|
55
|
-
# Set CLAUDE_MPM_HOOK_DEBUG=false to disable debug output
|
|
56
|
-
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
|
|
57
|
-
|
|
58
|
-
# Global singleton handler instance
|
|
59
|
-
_global_handler = None
|
|
60
|
-
_handler_lock = threading.Lock()
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class ClaudeHookHandler:
|
|
64
|
-
"""Refactored hook handler with service-oriented architecture.
|
|
65
|
-
|
|
66
|
-
WHY service-oriented approach:
|
|
67
|
-
- Modular design with clear service boundaries
|
|
68
|
-
- Each service handles a specific responsibility
|
|
69
|
-
- Easier to test, maintain, and extend
|
|
70
|
-
- Reduced complexity in main handler class
|
|
71
|
-
"""
|
|
72
|
-
|
|
73
|
-
def __init__(self):
|
|
74
|
-
# Initialize services
|
|
75
|
-
self.state_manager = StateManagerService()
|
|
76
|
-
self.connection_manager = ConnectionManagerService()
|
|
77
|
-
self.duplicate_detector = DuplicateEventDetector()
|
|
78
|
-
|
|
79
|
-
# Initialize extracted managers
|
|
80
|
-
self.memory_hook_manager = MemoryHookManager()
|
|
81
|
-
self.response_tracking_manager = ResponseTrackingManager()
|
|
82
|
-
self.event_handlers = EventHandlers(self)
|
|
83
|
-
|
|
84
|
-
# Initialize subagent processor with dependencies
|
|
85
|
-
self.subagent_processor = SubagentResponseProcessor(
|
|
86
|
-
self.state_manager, self.response_tracking_manager, self.connection_manager
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
def handle(self):
|
|
90
|
-
"""Process hook event with minimal overhead and timeout protection.
|
|
91
|
-
|
|
92
|
-
WHY this approach:
|
|
93
|
-
- Fast path processing for minimal latency (no blocking waits)
|
|
94
|
-
- Non-blocking Socket.IO connection and event emission
|
|
95
|
-
- Timeout protection prevents indefinite hangs
|
|
96
|
-
- Connection timeout prevents indefinite hangs
|
|
97
|
-
- Graceful degradation if Socket.IO unavailable
|
|
98
|
-
- Always continues regardless of event status
|
|
99
|
-
- Process exits after handling to prevent accumulation
|
|
100
|
-
"""
|
|
101
|
-
_continue_sent = False # Track if continue has been sent
|
|
102
|
-
|
|
103
|
-
def timeout_handler(signum, frame):
|
|
104
|
-
"""Handle timeout by forcing exit."""
|
|
105
|
-
nonlocal _continue_sent
|
|
106
|
-
if DEBUG:
|
|
107
|
-
print(f"Hook handler timeout (pid: {os.getpid()})", file=sys.stderr)
|
|
108
|
-
if not _continue_sent:
|
|
109
|
-
self._continue_execution()
|
|
110
|
-
_continue_sent = True
|
|
111
|
-
sys.exit(0)
|
|
112
|
-
|
|
113
|
-
try:
|
|
114
|
-
# Set a 10-second timeout for the entire operation
|
|
115
|
-
signal.signal(signal.SIGALRM, timeout_handler)
|
|
116
|
-
signal.alarm(10)
|
|
117
|
-
|
|
118
|
-
# Read and parse event
|
|
119
|
-
event = self._read_hook_event()
|
|
120
|
-
if not event:
|
|
121
|
-
if not _continue_sent:
|
|
122
|
-
self._continue_execution()
|
|
123
|
-
_continue_sent = True
|
|
124
|
-
return
|
|
125
|
-
|
|
126
|
-
# Check for duplicate events (same event within 100ms)
|
|
127
|
-
if self.duplicate_detector.is_duplicate(event):
|
|
128
|
-
if DEBUG:
|
|
129
|
-
print(
|
|
130
|
-
f"[{datetime.now(timezone.utc).isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})",
|
|
131
|
-
file=sys.stderr,
|
|
132
|
-
)
|
|
133
|
-
# Still need to output continue for this invocation
|
|
134
|
-
if not _continue_sent:
|
|
135
|
-
self._continue_execution()
|
|
136
|
-
_continue_sent = True
|
|
137
|
-
return
|
|
138
|
-
|
|
139
|
-
# Debug: Log that we're processing an event
|
|
140
|
-
if DEBUG:
|
|
141
|
-
hook_type = event.get("hook_event_name", "unknown")
|
|
142
|
-
print(
|
|
143
|
-
f"\n[{datetime.now(timezone.utc).isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})",
|
|
144
|
-
file=sys.stderr,
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
# Perform periodic cleanup if needed
|
|
148
|
-
if self.state_manager.increment_events_processed():
|
|
149
|
-
self.state_manager.cleanup_old_entries()
|
|
150
|
-
if DEBUG:
|
|
151
|
-
print(
|
|
152
|
-
f"🧹 Performed cleanup after {self.state_manager.events_processed} events",
|
|
153
|
-
file=sys.stderr,
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
# Route event to appropriate handler
|
|
157
|
-
self._route_event(event)
|
|
158
|
-
|
|
159
|
-
# Always continue execution (only if not already sent)
|
|
160
|
-
if not _continue_sent:
|
|
161
|
-
self._continue_execution()
|
|
162
|
-
_continue_sent = True
|
|
163
|
-
|
|
164
|
-
except Exception:
|
|
165
|
-
# Fail fast and silent (only send continue if not already sent)
|
|
166
|
-
if not _continue_sent:
|
|
167
|
-
self._continue_execution()
|
|
168
|
-
_continue_sent = True
|
|
169
|
-
finally:
|
|
170
|
-
# Cancel the alarm
|
|
171
|
-
signal.alarm(0)
|
|
172
|
-
|
|
173
|
-
def _read_hook_event(self) -> dict:
|
|
174
|
-
"""
|
|
175
|
-
Read and parse hook event from stdin with timeout.
|
|
176
|
-
|
|
177
|
-
WHY: Centralized event reading with error handling and timeout
|
|
178
|
-
ensures consistent parsing and validation while preventing
|
|
179
|
-
processes from hanging indefinitely on stdin.read().
|
|
180
|
-
|
|
181
|
-
Returns:
|
|
182
|
-
Parsed event dictionary or None if invalid/timeout
|
|
183
|
-
"""
|
|
184
|
-
try:
|
|
185
|
-
# Check if data is available on stdin with 1 second timeout
|
|
186
|
-
if sys.stdin.isatty():
|
|
187
|
-
# Interactive terminal - no data expected
|
|
188
|
-
return None
|
|
189
|
-
|
|
190
|
-
ready, _, _ = select.select([sys.stdin], [], [], 1.0)
|
|
191
|
-
if not ready:
|
|
192
|
-
# No data available within timeout
|
|
193
|
-
if DEBUG:
|
|
194
|
-
print("No hook event data received within timeout", file=sys.stderr)
|
|
195
|
-
return None
|
|
196
|
-
|
|
197
|
-
# Data is available, read it
|
|
198
|
-
event_data = sys.stdin.read()
|
|
199
|
-
if not event_data.strip():
|
|
200
|
-
# Empty or whitespace-only data
|
|
201
|
-
return None
|
|
202
|
-
|
|
203
|
-
return json.loads(event_data)
|
|
204
|
-
except (json.JSONDecodeError, ValueError) as e:
|
|
205
|
-
if DEBUG:
|
|
206
|
-
print(f"Failed to parse hook event: {e}", file=sys.stderr)
|
|
207
|
-
return None
|
|
208
|
-
except Exception as e:
|
|
209
|
-
if DEBUG:
|
|
210
|
-
print(f"Error reading hook event: {e}", file=sys.stderr)
|
|
211
|
-
return None
|
|
212
|
-
|
|
213
|
-
def _route_event(self, event: dict) -> None:
|
|
214
|
-
"""
|
|
215
|
-
Route event to appropriate handler based on type.
|
|
216
|
-
|
|
217
|
-
WHY: Centralized routing reduces complexity and makes
|
|
218
|
-
it easier to add new event types.
|
|
219
|
-
|
|
220
|
-
Args:
|
|
221
|
-
event: Hook event dictionary
|
|
222
|
-
"""
|
|
223
|
-
hook_type = event.get("hook_event_name", "unknown")
|
|
224
|
-
|
|
225
|
-
# Map event types to handlers
|
|
226
|
-
event_handlers = {
|
|
227
|
-
"UserPromptSubmit": self.event_handlers.handle_user_prompt_fast,
|
|
228
|
-
"PreToolUse": self.event_handlers.handle_pre_tool_fast,
|
|
229
|
-
"PostToolUse": self.event_handlers.handle_post_tool_fast,
|
|
230
|
-
"Notification": self.event_handlers.handle_notification_fast,
|
|
231
|
-
"Stop": self.event_handlers.handle_stop_fast,
|
|
232
|
-
"SubagentStop": self.handle_subagent_stop,
|
|
233
|
-
"AssistantResponse": self.event_handlers.handle_assistant_response,
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
# Call appropriate handler if exists
|
|
237
|
-
handler = event_handlers.get(hook_type)
|
|
238
|
-
if handler:
|
|
239
|
-
try:
|
|
240
|
-
handler(event)
|
|
241
|
-
except Exception as e:
|
|
242
|
-
if DEBUG:
|
|
243
|
-
print(f"Error handling {hook_type}: {e}", file=sys.stderr)
|
|
244
|
-
|
|
245
|
-
def handle_subagent_stop(self, event: dict):
|
|
246
|
-
"""Delegate subagent stop processing to the specialized processor."""
|
|
247
|
-
self.subagent_processor.process_subagent_stop(event)
|
|
248
|
-
|
|
249
|
-
def _continue_execution(self) -> None:
|
|
250
|
-
"""
|
|
251
|
-
Send continue action to Claude.
|
|
252
|
-
|
|
253
|
-
WHY: Centralized response ensures consistent format
|
|
254
|
-
and makes it easier to add response modifications.
|
|
255
|
-
"""
|
|
256
|
-
print(json.dumps({"action": "continue"}))
|
|
257
|
-
|
|
258
|
-
# Delegation methods for compatibility with event_handlers
|
|
259
|
-
def _track_delegation(self, session_id: str, agent_type: str, request_data=None):
|
|
260
|
-
"""Track delegation through state manager."""
|
|
261
|
-
self.state_manager.track_delegation(session_id, agent_type, request_data)
|
|
262
|
-
|
|
263
|
-
def _get_delegation_agent_type(self, session_id: str) -> str:
|
|
264
|
-
"""Get delegation agent type through state manager."""
|
|
265
|
-
return self.state_manager.get_delegation_agent_type(session_id)
|
|
266
|
-
|
|
267
|
-
def _get_git_branch(self, working_dir=None) -> str:
|
|
268
|
-
"""Get git branch through state manager."""
|
|
269
|
-
return self.state_manager.get_git_branch(working_dir)
|
|
270
|
-
|
|
271
|
-
def _emit_socketio_event(self, namespace: str, event: str, data: dict):
|
|
272
|
-
"""Emit event through connection manager."""
|
|
273
|
-
self.connection_manager.emit_event(namespace, event, data)
|
|
274
|
-
|
|
275
|
-
def __del__(self):
|
|
276
|
-
"""Cleanup on handler destruction."""
|
|
277
|
-
# Clean up connection manager if it exists
|
|
278
|
-
if hasattr(self, "connection_manager") and self.connection_manager:
|
|
279
|
-
try:
|
|
280
|
-
self.connection_manager.cleanup()
|
|
281
|
-
except Exception:
|
|
282
|
-
pass # Ignore cleanup errors during destruction
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
def main():
|
|
286
|
-
"""Entry point with singleton pattern and proper cleanup."""
|
|
287
|
-
global _global_handler
|
|
288
|
-
_continue_printed = False # Track if we've already printed continue
|
|
289
|
-
|
|
290
|
-
def cleanup_handler(signum=None, frame=None):
|
|
291
|
-
"""Cleanup handler for signals and exit."""
|
|
292
|
-
nonlocal _continue_printed
|
|
293
|
-
if DEBUG:
|
|
294
|
-
print(
|
|
295
|
-
f"Hook handler cleanup (pid: {os.getpid()}, signal: {signum})",
|
|
296
|
-
file=sys.stderr,
|
|
297
|
-
)
|
|
298
|
-
# Only output continue if we haven't already (i.e., if interrupted by signal)
|
|
299
|
-
if signum is not None and not _continue_printed:
|
|
300
|
-
print(json.dumps({"action": "continue"}))
|
|
301
|
-
_continue_printed = True
|
|
302
|
-
sys.exit(0)
|
|
303
|
-
|
|
304
|
-
# Register cleanup handlers
|
|
305
|
-
signal.signal(signal.SIGTERM, cleanup_handler)
|
|
306
|
-
signal.signal(signal.SIGINT, cleanup_handler)
|
|
307
|
-
# Don't register atexit handler since we're handling exit properly in main
|
|
308
|
-
|
|
309
|
-
try:
|
|
310
|
-
# Use singleton pattern to prevent creating multiple instances
|
|
311
|
-
with _handler_lock:
|
|
312
|
-
if _global_handler is None:
|
|
313
|
-
_global_handler = ClaudeHookHandler()
|
|
314
|
-
if DEBUG:
|
|
315
|
-
print(
|
|
316
|
-
f"✅ Created new ClaudeHookHandler singleton (pid: {os.getpid()})",
|
|
317
|
-
file=sys.stderr,
|
|
318
|
-
)
|
|
319
|
-
elif DEBUG:
|
|
320
|
-
print(
|
|
321
|
-
f"♻️ Reusing existing ClaudeHookHandler singleton (pid: {os.getpid()})",
|
|
322
|
-
file=sys.stderr,
|
|
323
|
-
)
|
|
324
|
-
|
|
325
|
-
handler = _global_handler
|
|
326
|
-
|
|
327
|
-
# Mark that handle() will print continue
|
|
328
|
-
handler.handle()
|
|
329
|
-
_continue_printed = True # Mark as printed since handle() always prints it
|
|
330
|
-
|
|
331
|
-
# handler.handle() already calls _continue_execution(), so we don't need to do it again
|
|
332
|
-
# Just exit cleanly
|
|
333
|
-
sys.exit(0)
|
|
334
|
-
|
|
335
|
-
except Exception as e:
|
|
336
|
-
# Only output continue if not already printed
|
|
337
|
-
if not _continue_printed:
|
|
338
|
-
print(json.dumps({"action": "continue"}))
|
|
339
|
-
_continue_printed = True
|
|
340
|
-
# Log error for debugging
|
|
341
|
-
if DEBUG:
|
|
342
|
-
print(f"Hook handler error: {e}", file=sys.stderr)
|
|
343
|
-
sys.exit(0) # Exit cleanly even on error
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if __name__ == "__main__":
|
|
347
|
-
main()
|