claude-mpm 4.1.6__py3-none-any.whl → 4.1.8__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/OUTPUT_STYLE.md +73 -0
- claude_mpm/agents/templates/agent-manager.json +1 -1
- claude_mpm/agents/templates/agent-manager.md +349 -34
- claude_mpm/cli/commands/configure.py +151 -2
- claude_mpm/cli/commands/configure_tui.py +5 -1
- claude_mpm/cli/parsers/configure_parser.py +23 -0
- claude_mpm/config/socketio_config.py +33 -4
- claude_mpm/dashboard/static/js/socket-client.js +40 -16
- claude_mpm/hooks/claude_hooks/installer.py +455 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +17 -0
- claude_mpm/services/agents/deployment/agent_config_provider.py +127 -27
- claude_mpm/services/diagnostics/checks/instructions_check.py +1 -3
- claude_mpm/services/event_bus/direct_relay.py +146 -11
- claude_mpm/services/socketio/handlers/connection_handler.py +3 -18
- claude_mpm/services/socketio/server/connection_manager.py +124 -63
- claude_mpm/services/socketio/server/core.py +34 -7
- claude_mpm/services/socketio/server/main.py +83 -21
- {claude_mpm-4.1.6.dist-info → claude_mpm-4.1.8.dist-info}/METADATA +1 -1
- {claude_mpm-4.1.6.dist-info → claude_mpm-4.1.8.dist-info}/RECORD +24 -22
- {claude_mpm-4.1.6.dist-info → claude_mpm-4.1.8.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.6.dist-info → claude_mpm-4.1.8.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.6.dist-info → claude_mpm-4.1.8.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.6.dist-info → claude_mpm-4.1.8.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hook installer for Claude MPM integration with Claude Code.
|
|
3
|
+
|
|
4
|
+
This module provides functionality to install, update, and manage
|
|
5
|
+
claude-mpm hooks in the Claude Code environment.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import shutil
|
|
11
|
+
import stat
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Dict, List, Tuple
|
|
14
|
+
|
|
15
|
+
from ...core.logger import get_logger
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class HookInstaller:
|
|
19
|
+
"""Manages installation and configuration of Claude MPM hooks."""
|
|
20
|
+
|
|
21
|
+
# Smart hook script template
|
|
22
|
+
SMART_HOOK_SCRIPT = """#!/bin/bash
|
|
23
|
+
# Claude MPM Smart Hook Handler
|
|
24
|
+
# This script dynamically finds and routes hook events to claude-mpm
|
|
25
|
+
# Works with pip installations, local development, and virtual environments
|
|
26
|
+
|
|
27
|
+
# Function to find claude-mpm installation
|
|
28
|
+
find_claude_mpm() {
|
|
29
|
+
# Method 1: Check if claude-mpm is installed via pip
|
|
30
|
+
if command -v claude-mpm &> /dev/null; then
|
|
31
|
+
# Get the actual path of the claude-mpm command
|
|
32
|
+
local cmd_path=$(command -v claude-mpm)
|
|
33
|
+
if [ -L "$cmd_path" ]; then
|
|
34
|
+
# Follow symlink
|
|
35
|
+
cmd_path=$(readlink -f "$cmd_path")
|
|
36
|
+
fi
|
|
37
|
+
# Extract the base directory (usually site-packages or venv)
|
|
38
|
+
local base_dir=$(python3 -c "import claude_mpm; import os; print(os.path.dirname(os.path.dirname(claude_mpm.__file__)))" 2>/dev/null)
|
|
39
|
+
if [ -n "$base_dir" ]; then
|
|
40
|
+
echo "$base_dir"
|
|
41
|
+
return 0
|
|
42
|
+
fi
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Method 2: Check common development locations
|
|
46
|
+
local dev_locations=(
|
|
47
|
+
"$HOME/Projects/claude-mpm"
|
|
48
|
+
"$HOME/projects/claude-mpm"
|
|
49
|
+
"$HOME/dev/claude-mpm"
|
|
50
|
+
"$HOME/Development/claude-mpm"
|
|
51
|
+
"$HOME/src/claude-mpm"
|
|
52
|
+
"$HOME/code/claude-mpm"
|
|
53
|
+
"$HOME/workspace/claude-mpm"
|
|
54
|
+
"$HOME/claude-mpm"
|
|
55
|
+
"$(pwd)/claude-mpm"
|
|
56
|
+
"$(pwd)"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
for loc in "${dev_locations[@]}"; do
|
|
60
|
+
if [ -f "$loc/src/claude_mpm/__init__.py" ]; then
|
|
61
|
+
echo "$loc"
|
|
62
|
+
return 0
|
|
63
|
+
fi
|
|
64
|
+
done
|
|
65
|
+
|
|
66
|
+
# Method 3: Try to find via Python import
|
|
67
|
+
local python_path=$(python3 -c "
|
|
68
|
+
try:
|
|
69
|
+
import claude_mpm
|
|
70
|
+
import os
|
|
71
|
+
# Get the package directory
|
|
72
|
+
pkg_dir = os.path.dirname(claude_mpm.__file__)
|
|
73
|
+
# Check if we're in a development install (src directory)
|
|
74
|
+
if 'src' in pkg_dir:
|
|
75
|
+
# Go up to find the project root
|
|
76
|
+
parts = pkg_dir.split(os.sep)
|
|
77
|
+
if 'src' in parts:
|
|
78
|
+
src_idx = parts.index('src')
|
|
79
|
+
project_root = os.sep.join(parts[:src_idx])
|
|
80
|
+
print(project_root)
|
|
81
|
+
else:
|
|
82
|
+
print(os.path.dirname(os.path.dirname(pkg_dir)))
|
|
83
|
+
else:
|
|
84
|
+
# Installed package - just return the package location
|
|
85
|
+
print(os.path.dirname(pkg_dir))
|
|
86
|
+
except:
|
|
87
|
+
pass
|
|
88
|
+
" 2>/dev/null)
|
|
89
|
+
|
|
90
|
+
if [ -n "$python_path" ]; then
|
|
91
|
+
echo "$python_path"
|
|
92
|
+
return 0
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Method 4: Search in PATH for claude-mpm installations
|
|
96
|
+
local IFS=':'
|
|
97
|
+
for path_dir in $PATH; do
|
|
98
|
+
if [ -f "$path_dir/claude-mpm" ]; then
|
|
99
|
+
# Found claude-mpm executable, try to find its package
|
|
100
|
+
local pkg_dir=$(cd "$path_dir" && python3 -c "import claude_mpm; import os; print(os.path.dirname(os.path.dirname(claude_mpm.__file__)))" 2>/dev/null)
|
|
101
|
+
if [ -n "$pkg_dir" ]; then
|
|
102
|
+
echo "$pkg_dir"
|
|
103
|
+
return 0
|
|
104
|
+
fi
|
|
105
|
+
fi
|
|
106
|
+
done
|
|
107
|
+
|
|
108
|
+
return 1
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# Function to setup Python environment
|
|
112
|
+
setup_python_env() {
|
|
113
|
+
local project_dir="$1"
|
|
114
|
+
|
|
115
|
+
# Check for virtual environment in the project
|
|
116
|
+
if [ -f "$project_dir/venv/bin/activate" ]; then
|
|
117
|
+
source "$project_dir/venv/bin/activate"
|
|
118
|
+
export PYTHON_CMD="$project_dir/venv/bin/python"
|
|
119
|
+
elif [ -f "$project_dir/.venv/bin/activate" ]; then
|
|
120
|
+
source "$project_dir/.venv/bin/activate"
|
|
121
|
+
export PYTHON_CMD="$project_dir/.venv/bin/python"
|
|
122
|
+
elif [ -n "$VIRTUAL_ENV" ]; then
|
|
123
|
+
# Already in a virtual environment
|
|
124
|
+
export PYTHON_CMD="$VIRTUAL_ENV/bin/python"
|
|
125
|
+
elif command -v python3 &> /dev/null; then
|
|
126
|
+
export PYTHON_CMD="python3"
|
|
127
|
+
else
|
|
128
|
+
export PYTHON_CMD="python"
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# Set PYTHONPATH for development installs
|
|
132
|
+
if [ -d "$project_dir/src" ]; then
|
|
133
|
+
export PYTHONPATH="$project_dir/src:$PYTHONPATH"
|
|
134
|
+
fi
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# Main execution
|
|
138
|
+
main() {
|
|
139
|
+
# Debug mode (can be disabled in production)
|
|
140
|
+
if [ "${CLAUDE_MPM_HOOK_DEBUG}" = "true" ]; then
|
|
141
|
+
echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Smart hook starting..." >> /tmp/claude-mpm-hook.log
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
# Find claude-mpm installation
|
|
145
|
+
PROJECT_DIR=$(find_claude_mpm)
|
|
146
|
+
|
|
147
|
+
if [ -z "$PROJECT_DIR" ]; then
|
|
148
|
+
# Claude MPM not found - return continue to not block Claude
|
|
149
|
+
if [ "${CLAUDE_MPM_HOOK_DEBUG}" = "true" ]; then
|
|
150
|
+
echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Claude MPM not found, continuing..." >> /tmp/claude-mpm-hook.log
|
|
151
|
+
fi
|
|
152
|
+
echo '{"action": "continue"}'
|
|
153
|
+
exit 0
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
if [ "${CLAUDE_MPM_HOOK_DEBUG}" = "true" ]; then
|
|
157
|
+
echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Found claude-mpm at: $PROJECT_DIR" >> /tmp/claude-mpm-hook.log
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
# Setup Python environment
|
|
161
|
+
setup_python_env "$PROJECT_DIR"
|
|
162
|
+
|
|
163
|
+
# Debug logging
|
|
164
|
+
if [ "${CLAUDE_MPM_HOOK_DEBUG}" = "true" ]; then
|
|
165
|
+
echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] PYTHON_CMD: $PYTHON_CMD" >> /tmp/claude-mpm-hook.log
|
|
166
|
+
echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] PYTHONPATH: $PYTHONPATH" >> /tmp/claude-mpm-hook.log
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# Set Socket.IO configuration for hook events
|
|
170
|
+
export CLAUDE_MPM_SOCKETIO_PORT="${CLAUDE_MPM_SOCKETIO_PORT:-8765}"
|
|
171
|
+
|
|
172
|
+
# Run the hook handler
|
|
173
|
+
if ! "$PYTHON_CMD" -m claude_mpm.hooks.claude_hooks.hook_handler "$@" 2>/tmp/claude-mpm-hook-error.log; then
|
|
174
|
+
# If the Python handler fails, always return continue to not block Claude
|
|
175
|
+
if [ "${CLAUDE_MPM_HOOK_DEBUG}" = "true" ]; then
|
|
176
|
+
echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Hook handler failed, see /tmp/claude-mpm-hook-error.log" >> /tmp/claude-mpm-hook.log
|
|
177
|
+
echo "[$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)] Error: $(cat /tmp/claude-mpm-hook-error.log 2>/dev/null | head -5)" >> /tmp/claude-mpm-hook.log
|
|
178
|
+
fi
|
|
179
|
+
echo '{"action": "continue"}'
|
|
180
|
+
exit 0
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
# Success
|
|
184
|
+
exit 0
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# Run main function
|
|
188
|
+
main "$@"
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
def __init__(self):
|
|
192
|
+
"""Initialize the hook installer."""
|
|
193
|
+
self.logger = get_logger(__name__)
|
|
194
|
+
self.claude_dir = Path.home() / ".claude"
|
|
195
|
+
self.hooks_dir = self.claude_dir / "hooks"
|
|
196
|
+
self.settings_file = self.claude_dir / "settings.json"
|
|
197
|
+
|
|
198
|
+
def install_hooks(self, force: bool = False) -> bool:
|
|
199
|
+
"""
|
|
200
|
+
Install Claude MPM hooks.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
force: Force reinstallation even if hooks already exist
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
True if installation successful, False otherwise
|
|
207
|
+
"""
|
|
208
|
+
try:
|
|
209
|
+
self.logger.info("Starting hook installation...")
|
|
210
|
+
|
|
211
|
+
# Create directories
|
|
212
|
+
self.claude_dir.mkdir(exist_ok=True)
|
|
213
|
+
self.hooks_dir.mkdir(exist_ok=True)
|
|
214
|
+
|
|
215
|
+
# Install smart hook script
|
|
216
|
+
hook_script_path = self.hooks_dir / "claude-mpm-hook.sh"
|
|
217
|
+
if hook_script_path.exists() and not force:
|
|
218
|
+
self.logger.info(
|
|
219
|
+
"Hook script already exists. Use --force to overwrite."
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
self._install_smart_hook_script(hook_script_path)
|
|
223
|
+
|
|
224
|
+
# Update Claude settings
|
|
225
|
+
self._update_claude_settings(hook_script_path)
|
|
226
|
+
|
|
227
|
+
# Install commands if available
|
|
228
|
+
self._install_commands()
|
|
229
|
+
|
|
230
|
+
self.logger.info("Hook installation completed successfully!")
|
|
231
|
+
return True
|
|
232
|
+
|
|
233
|
+
except Exception as e:
|
|
234
|
+
self.logger.error(f"Hook installation failed: {e}")
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
def _install_smart_hook_script(self, hook_script_path: Path) -> None:
|
|
238
|
+
"""Install the smart hook script that dynamically finds claude-mpm."""
|
|
239
|
+
self.logger.info(f"Installing smart hook script to {hook_script_path}")
|
|
240
|
+
|
|
241
|
+
# Write the smart hook script
|
|
242
|
+
with open(hook_script_path, "w") as f:
|
|
243
|
+
f.write(self.SMART_HOOK_SCRIPT)
|
|
244
|
+
|
|
245
|
+
# Make it executable
|
|
246
|
+
st = os.stat(hook_script_path)
|
|
247
|
+
os.chmod(hook_script_path, st.st_mode | stat.S_IEXEC)
|
|
248
|
+
|
|
249
|
+
self.logger.info("Smart hook script installed and made executable")
|
|
250
|
+
|
|
251
|
+
def _update_claude_settings(self, hook_script_path: Path) -> None:
|
|
252
|
+
"""Update Claude settings to use the installed hook."""
|
|
253
|
+
self.logger.info("Updating Claude settings...")
|
|
254
|
+
|
|
255
|
+
# Load existing settings or create new
|
|
256
|
+
if self.settings_file.exists():
|
|
257
|
+
with open(self.settings_file) as f:
|
|
258
|
+
settings = json.load(f)
|
|
259
|
+
self.logger.info("Found existing Claude settings")
|
|
260
|
+
else:
|
|
261
|
+
settings = {}
|
|
262
|
+
self.logger.info("Creating new Claude settings")
|
|
263
|
+
|
|
264
|
+
# Configure hooks
|
|
265
|
+
hook_config = {
|
|
266
|
+
"matcher": "*",
|
|
267
|
+
"hooks": [{"type": "command", "command": str(hook_script_path.absolute())}],
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# Update settings
|
|
271
|
+
if "hooks" not in settings:
|
|
272
|
+
settings["hooks"] = {}
|
|
273
|
+
|
|
274
|
+
# Add hooks for all event types
|
|
275
|
+
event_types = [
|
|
276
|
+
"UserPromptSubmit",
|
|
277
|
+
"PreToolUse",
|
|
278
|
+
"PostToolUse",
|
|
279
|
+
"Stop",
|
|
280
|
+
"SubagentStop",
|
|
281
|
+
]
|
|
282
|
+
|
|
283
|
+
for event_type in event_types:
|
|
284
|
+
settings["hooks"][event_type] = [hook_config]
|
|
285
|
+
|
|
286
|
+
# Write settings
|
|
287
|
+
with open(self.settings_file, "w") as f:
|
|
288
|
+
json.dump(settings, f, indent=2)
|
|
289
|
+
|
|
290
|
+
self.logger.info(f"Updated Claude settings at {self.settings_file}")
|
|
291
|
+
|
|
292
|
+
def _install_commands(self) -> None:
|
|
293
|
+
"""Install custom commands for Claude Code."""
|
|
294
|
+
# Find commands directory in the package
|
|
295
|
+
package_root = Path(__file__).parent.parent.parent.parent
|
|
296
|
+
commands_src = package_root / ".claude" / "commands"
|
|
297
|
+
|
|
298
|
+
if not commands_src.exists():
|
|
299
|
+
self.logger.debug(
|
|
300
|
+
"No commands directory found, skipping command installation"
|
|
301
|
+
)
|
|
302
|
+
return
|
|
303
|
+
|
|
304
|
+
commands_dst = self.claude_dir / "commands"
|
|
305
|
+
commands_dst.mkdir(exist_ok=True)
|
|
306
|
+
|
|
307
|
+
for cmd_file in commands_src.glob("*.md"):
|
|
308
|
+
dst_file = commands_dst / cmd_file.name
|
|
309
|
+
shutil.copy2(cmd_file, dst_file)
|
|
310
|
+
self.logger.info(f"Installed command: {cmd_file.name}")
|
|
311
|
+
|
|
312
|
+
def update_hooks(self) -> bool:
|
|
313
|
+
"""Update existing hooks to the latest version."""
|
|
314
|
+
return self.install_hooks(force=True)
|
|
315
|
+
|
|
316
|
+
def verify_hooks(self) -> Tuple[bool, List[str]]:
|
|
317
|
+
"""
|
|
318
|
+
Verify that hooks are properly installed.
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
Tuple of (is_valid, list_of_issues)
|
|
322
|
+
"""
|
|
323
|
+
issues = []
|
|
324
|
+
|
|
325
|
+
# Check hook script exists
|
|
326
|
+
hook_script_path = self.hooks_dir / "claude-mpm-hook.sh"
|
|
327
|
+
if not hook_script_path.exists():
|
|
328
|
+
issues.append(f"Hook script not found at {hook_script_path}")
|
|
329
|
+
|
|
330
|
+
# Check hook script is executable
|
|
331
|
+
elif not os.access(hook_script_path, os.X_OK):
|
|
332
|
+
issues.append(f"Hook script is not executable: {hook_script_path}")
|
|
333
|
+
|
|
334
|
+
# Check Claude settings
|
|
335
|
+
if not self.settings_file.exists():
|
|
336
|
+
issues.append(f"Claude settings file not found at {self.settings_file}")
|
|
337
|
+
else:
|
|
338
|
+
try:
|
|
339
|
+
with open(self.settings_file) as f:
|
|
340
|
+
settings = json.load(f)
|
|
341
|
+
|
|
342
|
+
if "hooks" not in settings:
|
|
343
|
+
issues.append("No hooks configured in Claude settings")
|
|
344
|
+
else:
|
|
345
|
+
# Check for required event types
|
|
346
|
+
required_events = ["Stop", "SubagentStop"]
|
|
347
|
+
for event in required_events:
|
|
348
|
+
if event not in settings["hooks"]:
|
|
349
|
+
issues.append(
|
|
350
|
+
f"Missing hook configuration for {event} event"
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
except json.JSONDecodeError as e:
|
|
354
|
+
issues.append(f"Invalid Claude settings JSON: {e}")
|
|
355
|
+
|
|
356
|
+
# Check if claude-mpm is accessible
|
|
357
|
+
try:
|
|
358
|
+
import claude_mpm
|
|
359
|
+
except ImportError:
|
|
360
|
+
issues.append("claude-mpm package not found in Python environment")
|
|
361
|
+
|
|
362
|
+
is_valid = len(issues) == 0
|
|
363
|
+
return is_valid, issues
|
|
364
|
+
|
|
365
|
+
def uninstall_hooks(self) -> bool:
|
|
366
|
+
"""
|
|
367
|
+
Remove Claude MPM hooks.
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
True if uninstallation successful, False otherwise
|
|
371
|
+
"""
|
|
372
|
+
try:
|
|
373
|
+
self.logger.info("Uninstalling hooks...")
|
|
374
|
+
|
|
375
|
+
# Remove hook script
|
|
376
|
+
hook_script_path = self.hooks_dir / "claude-mpm-hook.sh"
|
|
377
|
+
if hook_script_path.exists():
|
|
378
|
+
hook_script_path.unlink()
|
|
379
|
+
self.logger.info(f"Removed hook script: {hook_script_path}")
|
|
380
|
+
|
|
381
|
+
# Remove from Claude settings
|
|
382
|
+
if self.settings_file.exists():
|
|
383
|
+
with open(self.settings_file) as f:
|
|
384
|
+
settings = json.load(f)
|
|
385
|
+
|
|
386
|
+
if "hooks" in settings:
|
|
387
|
+
# Remove claude-mpm hooks
|
|
388
|
+
for event_type in list(settings["hooks"].keys()):
|
|
389
|
+
hooks = settings["hooks"][event_type]
|
|
390
|
+
# Filter out claude-mpm hooks
|
|
391
|
+
filtered_hooks = [
|
|
392
|
+
h
|
|
393
|
+
for h in hooks
|
|
394
|
+
if not (
|
|
395
|
+
isinstance(h, dict)
|
|
396
|
+
and h.get("hooks", [{}])[0]
|
|
397
|
+
.get("command", "")
|
|
398
|
+
.endswith("claude-mpm-hook.sh")
|
|
399
|
+
)
|
|
400
|
+
]
|
|
401
|
+
|
|
402
|
+
if filtered_hooks:
|
|
403
|
+
settings["hooks"][event_type] = filtered_hooks
|
|
404
|
+
else:
|
|
405
|
+
del settings["hooks"][event_type]
|
|
406
|
+
|
|
407
|
+
# Clean up empty hooks section
|
|
408
|
+
if not settings["hooks"]:
|
|
409
|
+
del settings["hooks"]
|
|
410
|
+
|
|
411
|
+
# Write back settings
|
|
412
|
+
with open(self.settings_file, "w") as f:
|
|
413
|
+
json.dump(settings, f, indent=2)
|
|
414
|
+
|
|
415
|
+
self.logger.info("Removed hooks from Claude settings")
|
|
416
|
+
|
|
417
|
+
self.logger.info("Hook uninstallation completed")
|
|
418
|
+
return True
|
|
419
|
+
|
|
420
|
+
except Exception as e:
|
|
421
|
+
self.logger.error(f"Hook uninstallation failed: {e}")
|
|
422
|
+
return False
|
|
423
|
+
|
|
424
|
+
def get_status(self) -> Dict[str, any]:
|
|
425
|
+
"""
|
|
426
|
+
Get the current status of hook installation.
|
|
427
|
+
|
|
428
|
+
Returns:
|
|
429
|
+
Dictionary with status information
|
|
430
|
+
"""
|
|
431
|
+
is_valid, issues = self.verify_hooks()
|
|
432
|
+
|
|
433
|
+
hook_script_path = self.hooks_dir / "claude-mpm-hook.sh"
|
|
434
|
+
|
|
435
|
+
status = {
|
|
436
|
+
"installed": hook_script_path.exists(),
|
|
437
|
+
"valid": is_valid,
|
|
438
|
+
"issues": issues,
|
|
439
|
+
"hook_script": str(hook_script_path) if hook_script_path.exists() else None,
|
|
440
|
+
"settings_file": (
|
|
441
|
+
str(self.settings_file) if self.settings_file.exists() else None
|
|
442
|
+
),
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
# Check Claude settings for hook configuration
|
|
446
|
+
if self.settings_file.exists():
|
|
447
|
+
try:
|
|
448
|
+
with open(self.settings_file) as f:
|
|
449
|
+
settings = json.load(f)
|
|
450
|
+
if "hooks" in settings:
|
|
451
|
+
status["configured_events"] = list(settings["hooks"].keys())
|
|
452
|
+
except:
|
|
453
|
+
pass
|
|
454
|
+
|
|
455
|
+
return status
|
|
@@ -170,11 +170,28 @@ class ConnectionManagerService:
|
|
|
170
170
|
# Publish to EventBus with topic format: hook.{event}
|
|
171
171
|
topic = f"hook.{event}"
|
|
172
172
|
self.event_bus.publish(topic, claude_event_data)
|
|
173
|
+
|
|
174
|
+
# Enhanced verification logging
|
|
173
175
|
if DEBUG:
|
|
174
176
|
print(f"✅ Published to EventBus: {topic}", file=sys.stderr)
|
|
177
|
+
# Get EventBus stats to verify publication
|
|
178
|
+
if hasattr(self.event_bus, "get_stats"):
|
|
179
|
+
stats = self.event_bus.get_stats()
|
|
180
|
+
print(
|
|
181
|
+
f"📊 EventBus stats after publish: {stats}", file=sys.stderr
|
|
182
|
+
)
|
|
183
|
+
# Log the number of data keys being published
|
|
184
|
+
if isinstance(claude_event_data, dict):
|
|
185
|
+
print(
|
|
186
|
+
f"📦 Published data keys: {list(claude_event_data.keys())}",
|
|
187
|
+
file=sys.stderr,
|
|
188
|
+
)
|
|
175
189
|
except Exception as e:
|
|
176
190
|
if DEBUG:
|
|
177
191
|
print(f"⚠️ Failed to publish to EventBus: {e}", file=sys.stderr)
|
|
192
|
+
import traceback
|
|
193
|
+
|
|
194
|
+
traceback.print_exc(file=sys.stderr)
|
|
178
195
|
|
|
179
196
|
# Warn if neither method is available
|
|
180
197
|
if not self.connection_pool and not self.event_bus and DEBUG:
|