claude-mpm 5.6.30__py3-none-any.whl → 5.6.32__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/hooks/claude_hooks/installer.py +88 -26
- claude_mpm/services/hook_installer_service.py +77 -8
- {claude_mpm-5.6.30.dist-info → claude_mpm-5.6.32.dist-info}/METADATA +1 -1
- {claude_mpm-5.6.30.dist-info → claude_mpm-5.6.32.dist-info}/RECORD +10 -10
- {claude_mpm-5.6.30.dist-info → claude_mpm-5.6.32.dist-info}/WHEEL +0 -0
- {claude_mpm-5.6.30.dist-info → claude_mpm-5.6.32.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.6.30.dist-info → claude_mpm-5.6.32.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.6.30.dist-info → claude_mpm-5.6.32.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.6.30.dist-info → claude_mpm-5.6.32.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.6.
|
|
1
|
+
5.6.32
|
|
@@ -203,10 +203,14 @@ main "$@"
|
|
|
203
203
|
import logging
|
|
204
204
|
|
|
205
205
|
self.logger = logging.getLogger(__name__)
|
|
206
|
-
|
|
206
|
+
# Use project-level paths, NEVER global ~/.claude/settings.json
|
|
207
|
+
# This ensures hooks are scoped to the current project only
|
|
208
|
+
self.project_root = Path.cwd()
|
|
209
|
+
self.claude_dir = self.project_root / ".claude"
|
|
207
210
|
self.hooks_dir = self.claude_dir / "hooks" # Kept for backward compatibility
|
|
208
|
-
# Use settings.json for
|
|
209
|
-
|
|
211
|
+
# Use settings.local.json for project-level hook settings
|
|
212
|
+
# Claude Code reads project-level settings from .claude/settings.local.json
|
|
213
|
+
self.settings_file = self.claude_dir / "settings.local.json"
|
|
210
214
|
# There is no legacy settings file - this was a bug where both pointed to same file
|
|
211
215
|
# Setting to None to disable cleanup that was deleting freshly installed hooks
|
|
212
216
|
self.old_settings_file = None
|
|
@@ -601,40 +605,98 @@ main "$@"
|
|
|
601
605
|
# Hook configuration for each event type
|
|
602
606
|
hook_command = {"type": "command", "command": str(hook_script_path.absolute())}
|
|
603
607
|
|
|
608
|
+
def is_our_hook(cmd: dict) -> bool:
|
|
609
|
+
"""Check if a hook command belongs to claude-mpm."""
|
|
610
|
+
if cmd.get("type") != "command":
|
|
611
|
+
return False
|
|
612
|
+
command = cmd.get("command", "")
|
|
613
|
+
return "claude-hook-handler.sh" in command or command.endswith(
|
|
614
|
+
"claude-mpm-hook.sh"
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
def merge_hooks_for_event(
|
|
618
|
+
existing_hooks: list, new_hook_command: dict, use_matcher: bool = True
|
|
619
|
+
) -> list:
|
|
620
|
+
"""Merge new hook command into existing hooks without duplication.
|
|
621
|
+
|
|
622
|
+
Args:
|
|
623
|
+
existing_hooks: Current hooks configuration for an event type
|
|
624
|
+
new_hook_command: The claude-mpm hook command to add
|
|
625
|
+
use_matcher: Whether to include matcher: "*" in the config
|
|
626
|
+
|
|
627
|
+
Returns:
|
|
628
|
+
Updated hooks list with our hook merged in
|
|
629
|
+
"""
|
|
630
|
+
# Check if our hook already exists in any existing hook config
|
|
631
|
+
our_hook_exists = False
|
|
632
|
+
|
|
633
|
+
for hook_config in existing_hooks:
|
|
634
|
+
if "hooks" in hook_config and isinstance(hook_config["hooks"], list):
|
|
635
|
+
for hook in hook_config["hooks"]:
|
|
636
|
+
if is_our_hook(hook):
|
|
637
|
+
# Update existing hook command path (in case it changed)
|
|
638
|
+
hook["command"] = new_hook_command["command"]
|
|
639
|
+
our_hook_exists = True
|
|
640
|
+
break
|
|
641
|
+
if our_hook_exists:
|
|
642
|
+
break
|
|
643
|
+
|
|
644
|
+
if our_hook_exists:
|
|
645
|
+
# Our hook already exists, just return the updated list
|
|
646
|
+
return existing_hooks
|
|
647
|
+
|
|
648
|
+
# Our hook doesn't exist - need to add it
|
|
649
|
+
# Strategy: Add our hook to the first "*" matcher config, or create new config
|
|
650
|
+
added = False
|
|
651
|
+
|
|
652
|
+
for hook_config in existing_hooks:
|
|
653
|
+
# Check if this config has matcher: "*" (or no matcher for simple events)
|
|
654
|
+
matcher = hook_config.get("matcher")
|
|
655
|
+
if matcher == "*" or (not use_matcher and matcher is None):
|
|
656
|
+
# Add our hook to this config's hooks array
|
|
657
|
+
if "hooks" not in hook_config:
|
|
658
|
+
hook_config["hooks"] = []
|
|
659
|
+
hook_config["hooks"].append(new_hook_command)
|
|
660
|
+
added = True
|
|
661
|
+
break
|
|
662
|
+
|
|
663
|
+
if not added:
|
|
664
|
+
# No suitable config found, create a new one
|
|
665
|
+
if use_matcher:
|
|
666
|
+
new_config = {"matcher": "*", "hooks": [new_hook_command]}
|
|
667
|
+
else:
|
|
668
|
+
new_config = {"hooks": [new_hook_command]}
|
|
669
|
+
existing_hooks.append(new_config)
|
|
670
|
+
|
|
671
|
+
return existing_hooks
|
|
672
|
+
|
|
604
673
|
# Tool-related events need a matcher string
|
|
605
674
|
tool_events = ["PreToolUse", "PostToolUse"]
|
|
606
675
|
for event_type in tool_events:
|
|
607
|
-
settings["hooks"]
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
612
|
-
]
|
|
676
|
+
existing = settings["hooks"].get(event_type, [])
|
|
677
|
+
settings["hooks"][event_type] = merge_hooks_for_event(
|
|
678
|
+
existing, hook_command, use_matcher=True
|
|
679
|
+
)
|
|
613
680
|
|
|
614
681
|
# Simple events (no subtypes, no matcher needed)
|
|
615
682
|
simple_events = ["Stop", "SubagentStop", "SubagentStart"]
|
|
616
683
|
for event_type in simple_events:
|
|
617
|
-
settings["hooks"]
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
]
|
|
684
|
+
existing = settings["hooks"].get(event_type, [])
|
|
685
|
+
settings["hooks"][event_type] = merge_hooks_for_event(
|
|
686
|
+
existing, hook_command, use_matcher=False
|
|
687
|
+
)
|
|
622
688
|
|
|
623
689
|
# SessionStart needs matcher for subtypes (startup, resume)
|
|
624
|
-
settings["hooks"]
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
}
|
|
629
|
-
]
|
|
690
|
+
existing = settings["hooks"].get("SessionStart", [])
|
|
691
|
+
settings["hooks"]["SessionStart"] = merge_hooks_for_event(
|
|
692
|
+
existing, hook_command, use_matcher=True
|
|
693
|
+
)
|
|
630
694
|
|
|
631
695
|
# UserPromptSubmit needs matcher for potential subtypes
|
|
632
|
-
settings["hooks"]
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}
|
|
637
|
-
]
|
|
696
|
+
existing = settings["hooks"].get("UserPromptSubmit", [])
|
|
697
|
+
settings["hooks"]["UserPromptSubmit"] = merge_hooks_for_event(
|
|
698
|
+
existing, hook_command, use_matcher=True
|
|
699
|
+
)
|
|
638
700
|
|
|
639
701
|
# Fix statusLine command to handle both output style schemas
|
|
640
702
|
self._fix_status_line(settings)
|
|
@@ -20,8 +20,13 @@ class HookInstallerService:
|
|
|
20
20
|
def __init__(self):
|
|
21
21
|
"""Initialize the hook installer service."""
|
|
22
22
|
self.logger = get_logger(__name__)
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
# Use project-level paths, NEVER global ~/.claude/settings.json
|
|
24
|
+
# This ensures hooks are scoped to the current project only
|
|
25
|
+
self.project_root = Path.cwd()
|
|
26
|
+
self.claude_dir = self.project_root / ".claude"
|
|
27
|
+
# Use settings.local.json for project-level hook settings
|
|
28
|
+
# Claude Code reads project-level settings from .claude/settings.local.json
|
|
29
|
+
self.settings_file = self.claude_dir / "settings.local.json"
|
|
25
30
|
|
|
26
31
|
def is_hooks_configured(self) -> bool:
|
|
27
32
|
"""Check if hooks are configured in Claude settings.
|
|
@@ -299,16 +304,77 @@ class HookInstallerService:
|
|
|
299
304
|
self.logger.debug("Creating new Claude settings")
|
|
300
305
|
|
|
301
306
|
# Configure hooks
|
|
302
|
-
|
|
303
|
-
"matcher": "*",
|
|
304
|
-
"hooks": [{"type": "command", "command": hook_script_path}],
|
|
305
|
-
}
|
|
307
|
+
new_hook_command = {"type": "command", "command": hook_script_path}
|
|
306
308
|
|
|
307
309
|
# Update settings
|
|
308
310
|
if "hooks" not in settings:
|
|
309
311
|
settings["hooks"] = {}
|
|
310
312
|
|
|
311
|
-
|
|
313
|
+
def is_our_hook(cmd: Dict[str, Any]) -> bool:
|
|
314
|
+
"""Check if a hook command belongs to claude-mpm."""
|
|
315
|
+
if cmd.get("type") != "command":
|
|
316
|
+
return False
|
|
317
|
+
command = cmd.get("command", "")
|
|
318
|
+
return (
|
|
319
|
+
"hook_wrapper.sh" in command
|
|
320
|
+
or "claude-hook-handler.sh" in command
|
|
321
|
+
or "claude-mpm" in command
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
def merge_hooks_for_event(
|
|
325
|
+
existing_hooks: list, hook_command: Dict[str, Any]
|
|
326
|
+
) -> list:
|
|
327
|
+
"""Merge new hook command into existing hooks without duplication.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
existing_hooks: Current hooks configuration for an event type
|
|
331
|
+
hook_command: The claude-mpm hook command to add
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
Updated hooks list with our hook merged in
|
|
335
|
+
"""
|
|
336
|
+
# Check if our hook already exists in any existing hook config
|
|
337
|
+
our_hook_exists = False
|
|
338
|
+
|
|
339
|
+
for hook_config in existing_hooks:
|
|
340
|
+
if "hooks" in hook_config and isinstance(
|
|
341
|
+
hook_config["hooks"], list
|
|
342
|
+
):
|
|
343
|
+
for hook in hook_config["hooks"]:
|
|
344
|
+
if is_our_hook(hook):
|
|
345
|
+
# Update existing hook command path (in case it changed)
|
|
346
|
+
hook["command"] = hook_command["command"]
|
|
347
|
+
our_hook_exists = True
|
|
348
|
+
break
|
|
349
|
+
if our_hook_exists:
|
|
350
|
+
break
|
|
351
|
+
|
|
352
|
+
if our_hook_exists:
|
|
353
|
+
# Our hook already exists, just return the updated list
|
|
354
|
+
return existing_hooks
|
|
355
|
+
|
|
356
|
+
# Our hook doesn't exist - need to add it
|
|
357
|
+
# Strategy: Add our hook to the first "*" matcher config, or create new
|
|
358
|
+
added = False
|
|
359
|
+
|
|
360
|
+
for hook_config in existing_hooks:
|
|
361
|
+
# Check if this config has matcher: "*"
|
|
362
|
+
if hook_config.get("matcher") == "*":
|
|
363
|
+
# Add our hook to this config's hooks array
|
|
364
|
+
if "hooks" not in hook_config:
|
|
365
|
+
hook_config["hooks"] = []
|
|
366
|
+
hook_config["hooks"].append(hook_command)
|
|
367
|
+
added = True
|
|
368
|
+
break
|
|
369
|
+
|
|
370
|
+
if not added:
|
|
371
|
+
# No suitable config found, create a new one
|
|
372
|
+
new_config = {"matcher": "*", "hooks": [hook_command]}
|
|
373
|
+
existing_hooks.append(new_config)
|
|
374
|
+
|
|
375
|
+
return existing_hooks
|
|
376
|
+
|
|
377
|
+
# Add hooks for all event types - MERGE instead of overwrite
|
|
312
378
|
for event_type in [
|
|
313
379
|
"UserPromptSubmit",
|
|
314
380
|
"PreToolUse",
|
|
@@ -316,7 +382,10 @@ class HookInstallerService:
|
|
|
316
382
|
"Stop",
|
|
317
383
|
"SubagentStop",
|
|
318
384
|
]:
|
|
319
|
-
settings["hooks"]
|
|
385
|
+
existing = settings["hooks"].get(event_type, [])
|
|
386
|
+
settings["hooks"][event_type] = merge_hooks_for_event(
|
|
387
|
+
existing, new_hook_command
|
|
388
|
+
)
|
|
320
389
|
|
|
321
390
|
# Write settings
|
|
322
391
|
with self.settings_file.open("w") as f:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=9JfxhnDtr-8l3kCP2U5TVXSErptHoga8m7XA8zqgGOc,4
|
|
2
|
-
claude_mpm/VERSION,sha256=
|
|
2
|
+
claude_mpm/VERSION,sha256=dAg_-LZ5hKzApBj_BHyWt6jpEC7yRMezwMk3MwtJm40,7
|
|
3
3
|
claude_mpm/__init__.py,sha256=AGfh00BHKvLYD-UVFw7qbKtl7NMRIzRXOWw7vEuZ-h4,2214
|
|
4
4
|
claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
|
|
5
5
|
claude_mpm/constants.py,sha256=pz3lTrZZR5HhV3eZzYtIbtBwWo7iM6pkBHP_ixxmI6Y,6827
|
|
@@ -432,7 +432,7 @@ claude_mpm/hooks/claude_hooks/correlation_manager.py,sha256=3n-RxzqE8egG4max_Ncp
|
|
|
432
432
|
claude_mpm/hooks/claude_hooks/event_handlers.py,sha256=ztkKTr5xFAY-K5gxhsXFtR_4tir3Cjx2l4auSYbJErU,46745
|
|
433
433
|
claude_mpm/hooks/claude_hooks/hook_handler.py,sha256=UbBypLK1xhm6NdBBLLjqlsjLGWg1GgW-XSfeR7gLAOc,28242
|
|
434
434
|
claude_mpm/hooks/claude_hooks/hook_wrapper.sh,sha256=XYkdYtcM0nfnwYvMdyIFCasr80ry3uI5-fLYsLtDGw4,2214
|
|
435
|
-
claude_mpm/hooks/claude_hooks/installer.py,sha256=
|
|
435
|
+
claude_mpm/hooks/claude_hooks/installer.py,sha256=SvIgxRMocQxnqNF3ZQfKH8zA-1b4lpiCA4vCI0vWOZI,38065
|
|
436
436
|
claude_mpm/hooks/claude_hooks/memory_integration.py,sha256=YOMD4Ah003uMh7A454w8ngLmKw8RUAEIHpEj-FRk3TI,10759
|
|
437
437
|
claude_mpm/hooks/claude_hooks/response_tracking.py,sha256=1KOGC19rYRNYbc1Tfe7FAP6AtvgOMSM5uEPxMi2N6-c,16323
|
|
438
438
|
claude_mpm/hooks/claude_hooks/tool_analysis.py,sha256=3_o2PP9D7wEMwLriCtIBOw0cj2fSZfepN7lI4P1meSQ,7862
|
|
@@ -483,7 +483,7 @@ claude_mpm/services/delegation_detector.py,sha256=ZpElqjhTbuEeeTjTMUsl-G1lHMJ9m1
|
|
|
483
483
|
claude_mpm/services/event_aggregator.py,sha256=V_5Wln1RzozLMDZawIPl5gSjIN5KHniNPaaSP11Lihc,20251
|
|
484
484
|
claude_mpm/services/event_log.py,sha256=pMKc8p2lXRoRi_cvVxaU1uNuUCNrGc0EXqBv8TeueyI,10043
|
|
485
485
|
claude_mpm/services/exceptions.py,sha256=5lVZETr_6-xk0ItH7BTfYUiX5RlckS1e8ah_UalYG9c,26475
|
|
486
|
-
claude_mpm/services/hook_installer_service.py,sha256=
|
|
486
|
+
claude_mpm/services/hook_installer_service.py,sha256=AFN5hXfj0oz-pzPNaPRUOAqWFNF9lIZE3i2vNTduYCE,22916
|
|
487
487
|
claude_mpm/services/hook_service.py,sha256=I6JILbackBsdvrDNQ9TeGSB7XNqozNRP26T4E9_ROtU,15693
|
|
488
488
|
claude_mpm/services/mcp_config_manager.py,sha256=pQcu5g3lHo082TMX0RDgnfspnvW8huiKysCnFGOD54A,57792
|
|
489
489
|
claude_mpm/services/mcp_service_verifier.py,sha256=C2DhHY9R4j91WQbe1XSpfYeHjxHg2UyiJ1VQsBgjnlc,25808
|
|
@@ -1102,10 +1102,10 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
|
|
|
1102
1102
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
|
1103
1103
|
claude_mpm/validation/agent_validator.py,sha256=GprtAvu80VyMXcKGsK_VhYiXWA6BjKHv7O6HKx0AB9w,20917
|
|
1104
1104
|
claude_mpm/validation/frontmatter_validator.py,sha256=YpJlYNNYcV8u6hIOi3_jaRsDnzhbcQpjCBE6eyBKaFY,7076
|
|
1105
|
-
claude_mpm-5.6.
|
|
1106
|
-
claude_mpm-5.6.
|
|
1107
|
-
claude_mpm-5.6.
|
|
1108
|
-
claude_mpm-5.6.
|
|
1109
|
-
claude_mpm-5.6.
|
|
1110
|
-
claude_mpm-5.6.
|
|
1111
|
-
claude_mpm-5.6.
|
|
1105
|
+
claude_mpm-5.6.32.dist-info/licenses/LICENSE,sha256=ca3y_Rk4aPrbF6f62z8Ht5MJM9OAvbGlHvEDcj9vUQ4,3867
|
|
1106
|
+
claude_mpm-5.6.32.dist-info/licenses/LICENSE-FAQ.md,sha256=TxfEkXVCK98RzDOer09puc7JVCP_q_bN4dHtZKHCMcM,5104
|
|
1107
|
+
claude_mpm-5.6.32.dist-info/METADATA,sha256=hRiW5VmkSFqGVVORVStb3BZ5YCGSa2LFW50eFGy3jSY,15245
|
|
1108
|
+
claude_mpm-5.6.32.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
1109
|
+
claude_mpm-5.6.32.dist-info/entry_points.txt,sha256=n-Uk4vwHPpuvu-g_I7-GHORzTnN_m6iyOsoLveKKD0E,228
|
|
1110
|
+
claude_mpm-5.6.32.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
|
1111
|
+
claude_mpm-5.6.32.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|