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.
Files changed (250) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_ENGINEER.md +286 -0
  3. claude_mpm/agents/BASE_PM.md +48 -17
  4. claude_mpm/agents/OUTPUT_STYLE.md +329 -11
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +227 -8
  6. claude_mpm/agents/agent_loader.py +17 -5
  7. claude_mpm/agents/frontmatter_validator.py +284 -253
  8. claude_mpm/agents/templates/agentic-coder-optimizer.json +9 -2
  9. claude_mpm/agents/templates/api_qa.json +7 -1
  10. claude_mpm/agents/templates/clerk-ops.json +8 -1
  11. claude_mpm/agents/templates/code_analyzer.json +4 -1
  12. claude_mpm/agents/templates/dart_engineer.json +11 -1
  13. claude_mpm/agents/templates/data_engineer.json +11 -1
  14. claude_mpm/agents/templates/documentation.json +6 -1
  15. claude_mpm/agents/templates/engineer.json +18 -1
  16. claude_mpm/agents/templates/gcp_ops_agent.json +8 -1
  17. claude_mpm/agents/templates/golang_engineer.json +11 -1
  18. claude_mpm/agents/templates/java_engineer.json +12 -2
  19. claude_mpm/agents/templates/local_ops_agent.json +1217 -6
  20. claude_mpm/agents/templates/nextjs_engineer.json +11 -1
  21. claude_mpm/agents/templates/ops.json +8 -1
  22. claude_mpm/agents/templates/php-engineer.json +11 -1
  23. claude_mpm/agents/templates/project_organizer.json +10 -3
  24. claude_mpm/agents/templates/prompt-engineer.json +5 -1
  25. claude_mpm/agents/templates/python_engineer.json +11 -1
  26. claude_mpm/agents/templates/qa.json +7 -1
  27. claude_mpm/agents/templates/react_engineer.json +11 -1
  28. claude_mpm/agents/templates/refactoring_engineer.json +8 -1
  29. claude_mpm/agents/templates/research.json +4 -1
  30. claude_mpm/agents/templates/ruby-engineer.json +11 -1
  31. claude_mpm/agents/templates/rust_engineer.json +11 -1
  32. claude_mpm/agents/templates/security.json +6 -1
  33. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  34. claude_mpm/agents/templates/ticketing.json +6 -1
  35. claude_mpm/agents/templates/typescript_engineer.json +11 -1
  36. claude_mpm/agents/templates/vercel_ops_agent.json +8 -1
  37. claude_mpm/agents/templates/version_control.json +8 -1
  38. claude_mpm/agents/templates/web_qa.json +7 -1
  39. claude_mpm/agents/templates/web_ui.json +11 -1
  40. claude_mpm/cli/__init__.py +34 -706
  41. claude_mpm/cli/commands/agent_manager.py +25 -12
  42. claude_mpm/cli/commands/agent_state_manager.py +186 -0
  43. claude_mpm/cli/commands/agents.py +204 -148
  44. claude_mpm/cli/commands/aggregate.py +7 -3
  45. claude_mpm/cli/commands/analyze.py +9 -4
  46. claude_mpm/cli/commands/analyze_code.py +7 -2
  47. claude_mpm/cli/commands/auto_configure.py +7 -9
  48. claude_mpm/cli/commands/config.py +47 -13
  49. claude_mpm/cli/commands/configure.py +294 -1788
  50. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  51. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  52. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  53. claude_mpm/cli/commands/configure_models.py +18 -0
  54. claude_mpm/cli/commands/configure_navigation.py +167 -0
  55. claude_mpm/cli/commands/configure_paths.py +104 -0
  56. claude_mpm/cli/commands/configure_persistence.py +254 -0
  57. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  58. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  59. claude_mpm/cli/commands/configure_validators.py +73 -0
  60. claude_mpm/cli/commands/local_deploy.py +537 -0
  61. claude_mpm/cli/commands/memory.py +54 -20
  62. claude_mpm/cli/commands/mpm_init.py +39 -25
  63. claude_mpm/cli/commands/mpm_init_handler.py +8 -3
  64. claude_mpm/cli/executor.py +202 -0
  65. claude_mpm/cli/helpers.py +105 -0
  66. claude_mpm/cli/interactive/__init__.py +3 -0
  67. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  68. claude_mpm/cli/parsers/__init__.py +7 -1
  69. claude_mpm/cli/parsers/base_parser.py +98 -3
  70. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  71. claude_mpm/cli/shared/output_formatters.py +28 -19
  72. claude_mpm/cli/startup.py +481 -0
  73. claude_mpm/cli/utils.py +52 -1
  74. claude_mpm/commands/mpm-help.md +3 -0
  75. claude_mpm/commands/mpm-version.md +113 -0
  76. claude_mpm/commands/mpm.md +1 -0
  77. claude_mpm/config/agent_config.py +2 -2
  78. claude_mpm/config/model_config.py +428 -0
  79. claude_mpm/core/base_service.py +13 -12
  80. claude_mpm/core/enums.py +452 -0
  81. claude_mpm/core/factories.py +1 -1
  82. claude_mpm/core/instruction_reinforcement_hook.py +2 -1
  83. claude_mpm/core/interactive_session.py +9 -3
  84. claude_mpm/core/logging_config.py +6 -2
  85. claude_mpm/core/oneshot_session.py +8 -4
  86. claude_mpm/core/optimized_agent_loader.py +3 -3
  87. claude_mpm/core/output_style_manager.py +12 -192
  88. claude_mpm/core/service_registry.py +5 -1
  89. claude_mpm/core/types.py +2 -9
  90. claude_mpm/core/typing_utils.py +7 -6
  91. claude_mpm/dashboard/static/js/dashboard.js +0 -14
  92. claude_mpm/dashboard/templates/index.html +3 -41
  93. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  94. claude_mpm/hooks/instruction_reinforcement.py +7 -2
  95. claude_mpm/models/resume_log.py +340 -0
  96. claude_mpm/services/agents/auto_config_manager.py +10 -11
  97. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  98. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  99. claude_mpm/services/agents/deployment/agent_validator.py +17 -1
  100. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  101. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  102. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  103. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
  104. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  105. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  106. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
  107. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  108. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
  109. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  110. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  111. claude_mpm/services/agents/local_template_manager.py +1 -1
  112. claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
  113. claude_mpm/services/agents/registry/modification_tracker.py +5 -2
  114. claude_mpm/services/command_handler_service.py +11 -5
  115. claude_mpm/services/core/interfaces/__init__.py +74 -2
  116. claude_mpm/services/core/interfaces/health.py +172 -0
  117. claude_mpm/services/core/interfaces/model.py +281 -0
  118. claude_mpm/services/core/interfaces/process.py +372 -0
  119. claude_mpm/services/core/interfaces/restart.py +307 -0
  120. claude_mpm/services/core/interfaces/stability.py +260 -0
  121. claude_mpm/services/core/models/__init__.py +33 -0
  122. claude_mpm/services/core/models/agent_config.py +12 -28
  123. claude_mpm/services/core/models/health.py +162 -0
  124. claude_mpm/services/core/models/process.py +235 -0
  125. claude_mpm/services/core/models/restart.py +302 -0
  126. claude_mpm/services/core/models/stability.py +264 -0
  127. claude_mpm/services/core/path_resolver.py +23 -7
  128. claude_mpm/services/diagnostics/__init__.py +2 -2
  129. claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
  130. claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
  131. claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
  132. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
  133. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  134. claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
  135. claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
  136. claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
  137. claude_mpm/services/diagnostics/checks/mcp_services_check.py +36 -31
  138. claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
  139. claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
  140. claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
  141. claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
  142. claude_mpm/services/diagnostics/models.py +19 -24
  143. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
  144. claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
  145. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  146. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  147. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  148. claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
  149. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  150. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  151. claude_mpm/services/local_ops/__init__.py +163 -0
  152. claude_mpm/services/local_ops/crash_detector.py +257 -0
  153. claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
  154. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  155. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  156. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  157. claude_mpm/services/local_ops/health_manager.py +430 -0
  158. claude_mpm/services/local_ops/log_monitor.py +396 -0
  159. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  160. claude_mpm/services/local_ops/process_manager.py +595 -0
  161. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  162. claude_mpm/services/local_ops/restart_manager.py +401 -0
  163. claude_mpm/services/local_ops/restart_policy.py +387 -0
  164. claude_mpm/services/local_ops/state_manager.py +372 -0
  165. claude_mpm/services/local_ops/unified_manager.py +600 -0
  166. claude_mpm/services/mcp_config_manager.py +9 -4
  167. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  168. claude_mpm/services/mcp_gateway/core/base.py +18 -31
  169. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
  170. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
  171. claude_mpm/services/memory_hook_service.py +4 -1
  172. claude_mpm/services/model/__init__.py +147 -0
  173. claude_mpm/services/model/base_provider.py +365 -0
  174. claude_mpm/services/model/claude_provider.py +412 -0
  175. claude_mpm/services/model/model_router.py +453 -0
  176. claude_mpm/services/model/ollama_provider.py +415 -0
  177. claude_mpm/services/monitor/daemon_manager.py +3 -2
  178. claude_mpm/services/monitor/handlers/dashboard.py +2 -1
  179. claude_mpm/services/monitor/handlers/hooks.py +2 -1
  180. claude_mpm/services/monitor/management/lifecycle.py +3 -2
  181. claude_mpm/services/monitor/server.py +2 -1
  182. claude_mpm/services/session_management_service.py +3 -2
  183. claude_mpm/services/session_manager.py +205 -1
  184. claude_mpm/services/shared/async_service_base.py +16 -27
  185. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  186. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  187. claude_mpm/services/socketio/handlers/hook.py +13 -2
  188. claude_mpm/services/socketio/handlers/registry.py +4 -2
  189. claude_mpm/services/socketio/server/main.py +10 -8
  190. claude_mpm/services/subprocess_launcher_service.py +14 -5
  191. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
  192. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
  193. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
  194. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
  195. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
  196. claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
  197. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
  198. claude_mpm/services/unified/deployment_strategies/local.py +6 -5
  199. claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
  200. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
  201. claude_mpm/services/unified/interfaces.py +3 -1
  202. claude_mpm/services/unified/unified_analyzer.py +14 -10
  203. claude_mpm/services/unified/unified_config.py +2 -1
  204. claude_mpm/services/unified/unified_deployment.py +9 -4
  205. claude_mpm/services/version_service.py +104 -1
  206. claude_mpm/skills/__init__.py +21 -0
  207. claude_mpm/skills/bundled/__init__.py +6 -0
  208. claude_mpm/skills/bundled/api-documentation.md +393 -0
  209. claude_mpm/skills/bundled/async-testing.md +571 -0
  210. claude_mpm/skills/bundled/code-review.md +143 -0
  211. claude_mpm/skills/bundled/database-migration.md +199 -0
  212. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  213. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  214. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  215. claude_mpm/skills/bundled/git-workflow.md +414 -0
  216. claude_mpm/skills/bundled/imagemagick.md +204 -0
  217. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  218. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  219. claude_mpm/skills/bundled/pdf.md +141 -0
  220. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  221. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  222. claude_mpm/skills/bundled/security-scanning.md +327 -0
  223. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  224. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  225. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  226. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  227. claude_mpm/skills/bundled/xlsx.md +157 -0
  228. claude_mpm/skills/registry.py +286 -0
  229. claude_mpm/skills/skill_manager.py +310 -0
  230. claude_mpm/tools/code_tree_analyzer.py +177 -141
  231. claude_mpm/tools/code_tree_events.py +4 -2
  232. claude_mpm/utils/agent_dependency_loader.py +2 -2
  233. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +117 -8
  234. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +238 -174
  235. claude_mpm/dashboard/static/css/code-tree.css +0 -1639
  236. claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
  237. claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
  238. claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
  239. claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
  240. claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
  241. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
  242. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  243. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
  244. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  245. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  246. claude_mpm/services/project/analyzer_refactored.py +0 -450
  247. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
  248. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
  249. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
  250. {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()