claude-mpm 5.4.3__py3-none-any.whl → 5.4.21__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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +4 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +166 -21
- claude_mpm/agents/agent_loader.py +3 -27
- claude_mpm/cli/__main__.py +4 -0
- claude_mpm/cli/chrome_devtools_installer.py +175 -0
- claude_mpm/cli/commands/agents.py +0 -31
- claude_mpm/cli/commands/auto_configure.py +210 -25
- claude_mpm/cli/commands/config.py +88 -2
- claude_mpm/cli/commands/configure.py +85 -43
- claude_mpm/cli/commands/configure_agent_display.py +3 -1
- claude_mpm/cli/commands/mpm_init/core.py +2 -45
- claude_mpm/cli/commands/skills.py +214 -189
- claude_mpm/cli/executor.py +3 -3
- claude_mpm/cli/parsers/agents_parser.py +0 -9
- claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
- claude_mpm/cli/parsers/config_parser.py +153 -83
- claude_mpm/cli/parsers/skills_parser.py +3 -2
- claude_mpm/cli/startup.py +490 -41
- claude_mpm/commands/mpm-config.md +265 -0
- claude_mpm/commands/mpm-help.md +14 -95
- claude_mpm/commands/mpm-organize.md +350 -153
- claude_mpm/core/framework/formatters/content_formatter.py +3 -13
- claude_mpm/core/framework_loader.py +4 -2
- claude_mpm/core/logger.py +13 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +176 -76
- claude_mpm/hooks/claude_hooks/hook_handler.py +2 -0
- claude_mpm/hooks/claude_hooks/installer.py +33 -10
- claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
- claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
- claude_mpm/hooks/memory_integration_hook.py +46 -1
- claude_mpm/init.py +0 -19
- claude_mpm/scripts/claude-hook-handler.sh +58 -18
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/agent_recommendation_service.py +6 -7
- claude_mpm/services/agents/agent_review_service.py +280 -0
- claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -3
- claude_mpm/services/agents/deployment/agent_template_builder.py +1 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +78 -9
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +13 -0
- claude_mpm/services/agents/git_source_manager.py +14 -0
- claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
- claude_mpm/services/agents/toolchain_detector.py +6 -3
- claude_mpm/services/command_deployment_service.py +81 -8
- claude_mpm/services/git/git_operations_service.py +93 -8
- claude_mpm/services/self_upgrade_service.py +120 -12
- claude_mpm/services/skills/__init__.py +3 -0
- claude_mpm/services/skills/git_skill_source_manager.py +32 -2
- claude_mpm/services/skills/selective_skill_deployer.py +704 -0
- claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
- claude_mpm/services/skills_deployer.py +126 -9
- {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.21.dist-info}/METADATA +47 -8
- {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.21.dist-info}/RECORD +58 -82
- {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.21.dist-info}/entry_points.txt +0 -3
- claude_mpm-5.4.21.dist-info/licenses/LICENSE +94 -0
- claude_mpm-5.4.21.dist-info/licenses/LICENSE-FAQ.md +153 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
- claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
- claude_mpm/agents/BASE_ENGINEER.md +0 -658
- claude_mpm/agents/BASE_OPS.md +0 -219
- claude_mpm/agents/BASE_PM.md +0 -480
- claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
- claude_mpm/agents/BASE_QA.md +0 -167
- claude_mpm/agents/BASE_RESEARCH.md +0 -53
- claude_mpm/agents/base_agent.json +0 -31
- claude_mpm/agents/base_agent_loader.py +0 -601
- claude_mpm/cli/commands/agents_detect.py +0 -380
- claude_mpm/cli/commands/agents_recommend.py +0 -309
- claude_mpm/cli/ticket_cli.py +0 -35
- claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
- claude_mpm/commands/mpm-agents-detect.md +0 -177
- claude_mpm/commands/mpm-agents-list.md +0 -131
- claude_mpm/commands/mpm-agents-recommend.md +0 -223
- claude_mpm/commands/mpm-config-view.md +0 -150
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
- claude_mpm-5.4.3.dist-info/licenses/LICENSE +0 -21
- {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.21.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.3.dist-info → claude_mpm-5.4.21.dist-info}/top_level.txt +0 -0
|
@@ -100,19 +100,9 @@ class ContentFormatter:
|
|
|
100
100
|
instructions += framework_content["actual_memories"]
|
|
101
101
|
instructions += "\n"
|
|
102
102
|
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if agent_memories:
|
|
107
|
-
instructions += "\n\n## Agent Memories\n\n"
|
|
108
|
-
instructions += "**The following are accumulated memories from specialized agents:**\n\n"
|
|
109
|
-
|
|
110
|
-
for agent_name in sorted(agent_memories.keys()):
|
|
111
|
-
memory_content = agent_memories[agent_name]
|
|
112
|
-
if memory_content:
|
|
113
|
-
instructions += f"### {agent_name.replace('_', ' ').title()} Agent Memory\n\n"
|
|
114
|
-
instructions += memory_content
|
|
115
|
-
instructions += "\n\n"
|
|
103
|
+
# NOTE: Agent memories are now injected at agent deployment time
|
|
104
|
+
# in agent_template_builder.py, not in PM instructions.
|
|
105
|
+
# This ensures each agent gets its own memory, not all memories embedded in PM.
|
|
116
106
|
|
|
117
107
|
# Add dynamic agent capabilities section
|
|
118
108
|
instructions += capabilities_section
|
|
@@ -255,10 +255,12 @@ class FrameworkLoader:
|
|
|
255
255
|
"""Load actual memories using the MemoryManager service."""
|
|
256
256
|
memories = self._memory_manager.load_memories()
|
|
257
257
|
|
|
258
|
+
# Only load PM memories (PM.md)
|
|
259
|
+
# Agent memories are loaded at deployment time in agent_template_builder.py
|
|
258
260
|
if "actual_memories" in memories:
|
|
259
261
|
content["actual_memories"] = memories["actual_memories"]
|
|
260
|
-
|
|
261
|
-
|
|
262
|
+
# NOTE: agent_memories are no longer loaded for PM instructions
|
|
263
|
+
# They are injected per-agent at deployment time
|
|
262
264
|
|
|
263
265
|
# === Agent Discovery Methods ===
|
|
264
266
|
|
claude_mpm/core/logger.py
CHANGED
|
@@ -175,6 +175,19 @@ def setup_logging(
|
|
|
175
175
|
Returns:
|
|
176
176
|
Configured logger
|
|
177
177
|
"""
|
|
178
|
+
# Detect deployment context for install-type-aware defaults
|
|
179
|
+
if level == "INFO": # Only override default, not explicit settings
|
|
180
|
+
from claude_mpm.core.unified_paths import DeploymentContext, PathContext
|
|
181
|
+
|
|
182
|
+
context = PathContext.detect_deployment_context()
|
|
183
|
+
if context in (
|
|
184
|
+
DeploymentContext.DEVELOPMENT,
|
|
185
|
+
DeploymentContext.EDITABLE_INSTALL,
|
|
186
|
+
):
|
|
187
|
+
level = "INFO" # Development: verbose logging
|
|
188
|
+
else:
|
|
189
|
+
level = "OFF" # Production installs: silent by default
|
|
190
|
+
|
|
178
191
|
logger = logging.getLogger(name)
|
|
179
192
|
|
|
180
193
|
# Handle OFF level
|
|
@@ -95,22 +95,29 @@ class EventHandlers:
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
# Store prompt for comprehensive response tracking if enabled
|
|
98
|
-
|
|
99
|
-
self.hook_handler
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
"
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
98
|
+
try:
|
|
99
|
+
rtm = getattr(self.hook_handler, "response_tracking_manager", None)
|
|
100
|
+
if (
|
|
101
|
+
rtm
|
|
102
|
+
and getattr(rtm, "response_tracking_enabled", False)
|
|
103
|
+
and getattr(rtm, "track_all_interactions", False)
|
|
104
|
+
):
|
|
105
|
+
session_id = event.get("session_id", "")
|
|
106
|
+
if session_id:
|
|
107
|
+
pending_prompts = getattr(self.hook_handler, "pending_prompts", {})
|
|
108
|
+
pending_prompts[session_id] = {
|
|
109
|
+
"prompt": prompt,
|
|
110
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
111
|
+
"working_directory": working_dir,
|
|
112
|
+
}
|
|
113
|
+
if DEBUG:
|
|
114
|
+
print(
|
|
115
|
+
f"Stored prompt for comprehensive tracking: session {session_id[:8]}...",
|
|
116
|
+
file=sys.stderr,
|
|
117
|
+
)
|
|
118
|
+
except Exception:
|
|
119
|
+
# Response tracking is optional - silently continue if it fails
|
|
120
|
+
pass
|
|
114
121
|
|
|
115
122
|
# Emit normalized event (namespace no longer needed with normalized events)
|
|
116
123
|
self.hook_handler._emit_socketio_event("", "user_prompt", prompt_data)
|
|
@@ -244,8 +251,11 @@ class EventHandlers:
|
|
|
244
251
|
f" - Request data keys: {list(request_data.keys())}",
|
|
245
252
|
file=sys.stderr,
|
|
246
253
|
)
|
|
254
|
+
delegation_requests = getattr(
|
|
255
|
+
self.hook_handler, "delegation_requests", {}
|
|
256
|
+
)
|
|
247
257
|
print(
|
|
248
|
-
f" - delegation_requests size: {len(
|
|
258
|
+
f" - delegation_requests size: {len(delegation_requests)}",
|
|
249
259
|
file=sys.stderr,
|
|
250
260
|
)
|
|
251
261
|
|
|
@@ -257,9 +267,13 @@ class EventHandlers:
|
|
|
257
267
|
)
|
|
258
268
|
|
|
259
269
|
# Trigger memory pre-delegation hook
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
270
|
+
try:
|
|
271
|
+
mhm = getattr(self.hook_handler, "memory_hook_manager", None)
|
|
272
|
+
if mhm and hasattr(mhm, "trigger_pre_delegation_hook"):
|
|
273
|
+
mhm.trigger_pre_delegation_hook(agent_type, tool_input, session_id)
|
|
274
|
+
except Exception:
|
|
275
|
+
# Memory hooks are optional
|
|
276
|
+
pass
|
|
263
277
|
|
|
264
278
|
# Emit a subagent_start event for better tracking
|
|
265
279
|
subagent_start_data = {
|
|
@@ -441,6 +455,11 @@ class EventHandlers:
|
|
|
441
455
|
),
|
|
442
456
|
}
|
|
443
457
|
|
|
458
|
+
# Include full output for file operations (Read, Edit, Write)
|
|
459
|
+
# so frontend can display file content
|
|
460
|
+
if tool_name in ("Read", "Edit", "Write", "Grep", "Glob") and "output" in event:
|
|
461
|
+
post_tool_data["output"] = event["output"]
|
|
462
|
+
|
|
444
463
|
# Add correlation_id if available for correlation with pre_tool
|
|
445
464
|
if tool_call_id:
|
|
446
465
|
post_tool_data["correlation_id"] = tool_call_id
|
|
@@ -451,14 +470,27 @@ class EventHandlers:
|
|
|
451
470
|
agent_type = self.hook_handler._get_delegation_agent_type(session_id)
|
|
452
471
|
|
|
453
472
|
# Trigger memory post-delegation hook
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
473
|
+
try:
|
|
474
|
+
mhm = getattr(self.hook_handler, "memory_hook_manager", None)
|
|
475
|
+
if mhm and hasattr(mhm, "trigger_post_delegation_hook"):
|
|
476
|
+
mhm.trigger_post_delegation_hook(agent_type, event, session_id)
|
|
477
|
+
except Exception:
|
|
478
|
+
# Memory hooks are optional
|
|
479
|
+
pass
|
|
457
480
|
|
|
458
481
|
# Track agent response if response tracking is enabled
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
482
|
+
try:
|
|
483
|
+
rtm = getattr(self.hook_handler, "response_tracking_manager", None)
|
|
484
|
+
if rtm and hasattr(rtm, "track_agent_response"):
|
|
485
|
+
delegation_requests = getattr(
|
|
486
|
+
self.hook_handler, "delegation_requests", {}
|
|
487
|
+
)
|
|
488
|
+
rtm.track_agent_response(
|
|
489
|
+
session_id, agent_type, event, delegation_requests
|
|
490
|
+
)
|
|
491
|
+
except Exception:
|
|
492
|
+
# Response tracking is optional
|
|
493
|
+
pass
|
|
462
494
|
|
|
463
495
|
self.hook_handler._emit_socketio_event("", "post_tool", post_tool_data)
|
|
464
496
|
|
|
@@ -519,9 +551,14 @@ class EventHandlers:
|
|
|
519
551
|
self._log_stop_event_debug(event, session_id, metadata)
|
|
520
552
|
|
|
521
553
|
# Track response if enabled
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
554
|
+
try:
|
|
555
|
+
rtm = getattr(self.hook_handler, "response_tracking_manager", None)
|
|
556
|
+
if rtm and hasattr(rtm, "track_stop_response"):
|
|
557
|
+
pending_prompts = getattr(self.hook_handler, "pending_prompts", {})
|
|
558
|
+
rtm.track_stop_response(event, session_id, metadata, pending_prompts)
|
|
559
|
+
except Exception:
|
|
560
|
+
# Response tracking is optional
|
|
561
|
+
pass
|
|
525
562
|
|
|
526
563
|
# Emit stop event to Socket.IO
|
|
527
564
|
self._emit_stop_event(event, session_id, metadata)
|
|
@@ -544,15 +581,27 @@ class EventHandlers:
|
|
|
544
581
|
self, event: dict, session_id: str, metadata: dict
|
|
545
582
|
) -> None:
|
|
546
583
|
"""Log debug information for stop events."""
|
|
584
|
+
try:
|
|
585
|
+
rtm = getattr(self.hook_handler, "response_tracking_manager", None)
|
|
586
|
+
tracking_enabled = (
|
|
587
|
+
getattr(rtm, "response_tracking_enabled", False) if rtm else False
|
|
588
|
+
)
|
|
589
|
+
tracker_exists = (
|
|
590
|
+
getattr(rtm, "response_tracker", None) is not None if rtm else False
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
print(
|
|
594
|
+
f" - response_tracking_enabled: {tracking_enabled}",
|
|
595
|
+
file=sys.stderr,
|
|
596
|
+
)
|
|
597
|
+
print(
|
|
598
|
+
f" - response_tracker exists: {tracker_exists}",
|
|
599
|
+
file=sys.stderr,
|
|
600
|
+
)
|
|
601
|
+
except Exception:
|
|
602
|
+
# If debug logging fails, just skip it
|
|
603
|
+
pass
|
|
547
604
|
|
|
548
|
-
print(
|
|
549
|
-
f" - response_tracking_enabled: {self.hook_handler.response_tracking_manager.response_tracking_enabled}",
|
|
550
|
-
file=sys.stderr,
|
|
551
|
-
)
|
|
552
|
-
print(
|
|
553
|
-
f" - response_tracker exists: {self.hook_handler.response_tracking_manager.response_tracker is not None}",
|
|
554
|
-
file=sys.stderr,
|
|
555
|
-
)
|
|
556
605
|
print(
|
|
557
606
|
f" - session_id: {session_id[:8] if session_id else 'None'}...",
|
|
558
607
|
file=sys.stderr,
|
|
@@ -600,15 +649,22 @@ class EventHandlers:
|
|
|
600
649
|
git_branch: str,
|
|
601
650
|
):
|
|
602
651
|
"""Handle response tracking for subagent stop events with fuzzy matching."""
|
|
603
|
-
|
|
604
|
-
self.hook_handler
|
|
605
|
-
|
|
606
|
-
|
|
652
|
+
try:
|
|
653
|
+
rtm = getattr(self.hook_handler, "response_tracking_manager", None)
|
|
654
|
+
if not (
|
|
655
|
+
rtm
|
|
656
|
+
and getattr(rtm, "response_tracking_enabled", False)
|
|
657
|
+
and getattr(rtm, "response_tracker", None)
|
|
658
|
+
):
|
|
659
|
+
return
|
|
660
|
+
except Exception:
|
|
661
|
+
# Response tracking is optional
|
|
607
662
|
return
|
|
608
663
|
|
|
609
664
|
try:
|
|
610
665
|
# Get the original request data (with fuzzy matching fallback)
|
|
611
|
-
|
|
666
|
+
delegation_requests = getattr(self.hook_handler, "delegation_requests", {})
|
|
667
|
+
request_info = delegation_requests.get(session_id)
|
|
612
668
|
|
|
613
669
|
# If exact match fails, try partial matching
|
|
614
670
|
if not request_info and session_id:
|
|
@@ -618,7 +674,7 @@ class EventHandlers:
|
|
|
618
674
|
file=sys.stderr,
|
|
619
675
|
)
|
|
620
676
|
# Try to find a session that matches the first 8-16 characters
|
|
621
|
-
for stored_sid in list(
|
|
677
|
+
for stored_sid in list(delegation_requests.keys()):
|
|
622
678
|
if (
|
|
623
679
|
stored_sid.startswith(session_id[:8])
|
|
624
680
|
or session_id.startswith(stored_sid[:8])
|
|
@@ -633,17 +689,13 @@ class EventHandlers:
|
|
|
633
689
|
f" - ✅ Fuzzy match found: {stored_sid[:16]}...",
|
|
634
690
|
file=sys.stderr,
|
|
635
691
|
)
|
|
636
|
-
request_info =
|
|
637
|
-
stored_sid
|
|
638
|
-
)
|
|
692
|
+
request_info = delegation_requests.get(stored_sid)
|
|
639
693
|
# Update the key to use the current session_id for consistency
|
|
640
694
|
if request_info:
|
|
641
|
-
|
|
642
|
-
request_info
|
|
643
|
-
)
|
|
695
|
+
delegation_requests[session_id] = request_info
|
|
644
696
|
# Optionally remove the old key to avoid duplicates
|
|
645
697
|
if stored_sid != session_id:
|
|
646
|
-
del
|
|
698
|
+
del delegation_requests[stored_sid]
|
|
647
699
|
break
|
|
648
700
|
|
|
649
701
|
if request_info:
|
|
@@ -691,23 +743,31 @@ class EventHandlers:
|
|
|
691
743
|
)
|
|
692
744
|
|
|
693
745
|
# Track the response
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
response=response_text,
|
|
698
|
-
session_id=session_id,
|
|
699
|
-
metadata=metadata,
|
|
746
|
+
rtm = getattr(self.hook_handler, "response_tracking_manager", None)
|
|
747
|
+
response_tracker = (
|
|
748
|
+
getattr(rtm, "response_tracker", None) if rtm else None
|
|
700
749
|
)
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
750
|
+
if response_tracker and hasattr(response_tracker, "track_response"):
|
|
751
|
+
file_path = response_tracker.track_response(
|
|
752
|
+
agent_name=agent_type,
|
|
753
|
+
request=full_request,
|
|
754
|
+
response=response_text,
|
|
755
|
+
session_id=session_id,
|
|
756
|
+
metadata=metadata,
|
|
706
757
|
)
|
|
707
758
|
|
|
759
|
+
if file_path and DEBUG:
|
|
760
|
+
print(
|
|
761
|
+
f"✅ Tracked {agent_type} agent response on SubagentStop: {file_path.name}",
|
|
762
|
+
file=sys.stderr,
|
|
763
|
+
)
|
|
764
|
+
|
|
708
765
|
# Clean up the request data
|
|
709
|
-
|
|
710
|
-
|
|
766
|
+
delegation_requests = getattr(
|
|
767
|
+
self.hook_handler, "delegation_requests", {}
|
|
768
|
+
)
|
|
769
|
+
if session_id in delegation_requests:
|
|
770
|
+
del delegation_requests[session_id]
|
|
711
771
|
|
|
712
772
|
elif DEBUG:
|
|
713
773
|
print(
|
|
@@ -731,9 +791,14 @@ class EventHandlers:
|
|
|
731
791
|
- Essential for comprehensive monitoring of Claude interactions
|
|
732
792
|
"""
|
|
733
793
|
# Track the response for logging
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
794
|
+
try:
|
|
795
|
+
rtm = getattr(self.hook_handler, "response_tracking_manager", None)
|
|
796
|
+
if rtm and hasattr(rtm, "track_assistant_response"):
|
|
797
|
+
pending_prompts = getattr(self.hook_handler, "pending_prompts", {})
|
|
798
|
+
rtm.track_assistant_response(event, pending_prompts)
|
|
799
|
+
except Exception:
|
|
800
|
+
# Response tracking is optional
|
|
801
|
+
pass
|
|
737
802
|
|
|
738
803
|
# Get working directory and git branch
|
|
739
804
|
working_dir = event.get("cwd", "")
|
|
@@ -763,16 +828,21 @@ class EventHandlers:
|
|
|
763
828
|
}
|
|
764
829
|
|
|
765
830
|
# Check if this is a response to a tracked prompt
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
831
|
+
try:
|
|
832
|
+
pending_prompts = getattr(self.hook_handler, "pending_prompts", {})
|
|
833
|
+
if session_id in pending_prompts:
|
|
834
|
+
prompt_data = pending_prompts[session_id]
|
|
835
|
+
assistant_response_data["original_prompt"] = prompt_data.get(
|
|
836
|
+
"prompt", ""
|
|
837
|
+
)[:200]
|
|
838
|
+
assistant_response_data["prompt_timestamp"] = prompt_data.get(
|
|
839
|
+
"timestamp", ""
|
|
840
|
+
)
|
|
841
|
+
assistant_response_data["is_tracked_response"] = True
|
|
842
|
+
else:
|
|
843
|
+
assistant_response_data["is_tracked_response"] = False
|
|
844
|
+
except Exception:
|
|
845
|
+
# If prompt lookup fails, just mark as not tracked
|
|
776
846
|
assistant_response_data["is_tracked_response"] = False
|
|
777
847
|
|
|
778
848
|
# Debug logging
|
|
@@ -786,3 +856,33 @@ class EventHandlers:
|
|
|
786
856
|
self.hook_handler._emit_socketio_event(
|
|
787
857
|
"", "assistant_response", assistant_response_data
|
|
788
858
|
)
|
|
859
|
+
|
|
860
|
+
def handle_session_start_fast(self, event):
|
|
861
|
+
"""Handle session start events for tracking conversation sessions.
|
|
862
|
+
|
|
863
|
+
WHY track session starts:
|
|
864
|
+
- Provides visibility into new conversation sessions
|
|
865
|
+
- Enables tracking of session lifecycle and duration
|
|
866
|
+
- Useful for monitoring concurrent sessions and resource usage
|
|
867
|
+
"""
|
|
868
|
+
session_id = event.get("session_id", "")
|
|
869
|
+
working_dir = event.get("cwd", "")
|
|
870
|
+
git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
|
|
871
|
+
|
|
872
|
+
session_start_data = {
|
|
873
|
+
"session_id": session_id,
|
|
874
|
+
"working_directory": working_dir,
|
|
875
|
+
"git_branch": git_branch,
|
|
876
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
877
|
+
"hook_event_name": "SessionStart",
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
# Debug logging
|
|
881
|
+
if DEBUG:
|
|
882
|
+
print(
|
|
883
|
+
f"Hook handler: Processing SessionStart - session: '{session_id}'",
|
|
884
|
+
file=sys.stderr,
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
# Emit normalized event
|
|
888
|
+
self.hook_handler._emit_socketio_event("", "session_start", session_start_data)
|
|
@@ -417,6 +417,8 @@ class ClaudeHookHandler:
|
|
|
417
417
|
"Notification": self.event_handlers.handle_notification_fast,
|
|
418
418
|
"Stop": self.event_handlers.handle_stop_fast,
|
|
419
419
|
"SubagentStop": self.event_handlers.handle_subagent_stop_fast,
|
|
420
|
+
"SubagentStart": self.event_handlers.handle_session_start_fast,
|
|
421
|
+
"SessionStart": self.event_handlers.handle_session_start_fast,
|
|
420
422
|
"AssistantResponse": self.event_handlers.handle_assistant_response,
|
|
421
423
|
}
|
|
422
424
|
|
|
@@ -202,8 +202,9 @@ main "$@"
|
|
|
202
202
|
self.hooks_dir = self.claude_dir / "hooks" # Kept for backward compatibility
|
|
203
203
|
# Use settings.json for hooks (Claude Code reads from this file)
|
|
204
204
|
self.settings_file = self.claude_dir / "settings.json"
|
|
205
|
-
#
|
|
206
|
-
|
|
205
|
+
# There is no legacy settings file - this was a bug where both pointed to same file
|
|
206
|
+
# Setting to None to disable cleanup that was deleting freshly installed hooks
|
|
207
|
+
self.old_settings_file = None
|
|
207
208
|
self._claude_version: Optional[str] = None
|
|
208
209
|
self._hook_script_path: Optional[Path] = None
|
|
209
210
|
|
|
@@ -462,7 +463,9 @@ main "$@"
|
|
|
462
463
|
|
|
463
464
|
def _cleanup_old_settings(self) -> None:
|
|
464
465
|
"""Remove hooks from old settings.json file if present."""
|
|
465
|
-
|
|
466
|
+
# No-op: old_settings_file was pointing to same file as settings_file (bug)
|
|
467
|
+
# This was causing freshly installed hooks to be immediately deleted
|
|
468
|
+
if self.old_settings_file is None or not self.old_settings_file.exists():
|
|
466
469
|
return
|
|
467
470
|
|
|
468
471
|
try:
|
|
@@ -518,15 +521,31 @@ main "$@"
|
|
|
518
521
|
}
|
|
519
522
|
]
|
|
520
523
|
|
|
521
|
-
#
|
|
522
|
-
|
|
523
|
-
for event_type in
|
|
524
|
+
# Simple events (no subtypes, no matcher needed)
|
|
525
|
+
simple_events = ["Stop", "SubagentStop", "SubagentStart"]
|
|
526
|
+
for event_type in simple_events:
|
|
524
527
|
settings["hooks"][event_type] = [
|
|
525
528
|
{
|
|
526
529
|
"hooks": [hook_command],
|
|
527
530
|
}
|
|
528
531
|
]
|
|
529
532
|
|
|
533
|
+
# SessionStart needs matcher for subtypes (startup, resume)
|
|
534
|
+
settings["hooks"]["SessionStart"] = [
|
|
535
|
+
{
|
|
536
|
+
"matcher": "*", # Match all SessionStart subtypes
|
|
537
|
+
"hooks": [hook_command],
|
|
538
|
+
}
|
|
539
|
+
]
|
|
540
|
+
|
|
541
|
+
# UserPromptSubmit needs matcher for potential subtypes
|
|
542
|
+
settings["hooks"]["UserPromptSubmit"] = [
|
|
543
|
+
{
|
|
544
|
+
"matcher": "*",
|
|
545
|
+
"hooks": [hook_command],
|
|
546
|
+
}
|
|
547
|
+
]
|
|
548
|
+
|
|
530
549
|
# Write settings to settings.json
|
|
531
550
|
with self.settings_file.open("w") as f:
|
|
532
551
|
json.dump(settings, f, indent=2)
|
|
@@ -650,9 +669,13 @@ main "$@"
|
|
|
650
669
|
old_script.unlink()
|
|
651
670
|
self.logger.info(f"Removed old deployed script: {old_script}")
|
|
652
671
|
|
|
653
|
-
# Remove from Claude settings
|
|
654
|
-
|
|
655
|
-
|
|
672
|
+
# Remove from Claude settings
|
|
673
|
+
settings_paths = [self.settings_file]
|
|
674
|
+
if self.old_settings_file is not None:
|
|
675
|
+
settings_paths.append(self.old_settings_file)
|
|
676
|
+
|
|
677
|
+
for settings_path in settings_paths:
|
|
678
|
+
if settings_path and settings_path.exists():
|
|
656
679
|
with settings_path.open() as f:
|
|
657
680
|
settings = json.load(f)
|
|
658
681
|
|
|
@@ -763,7 +786,7 @@ main "$@"
|
|
|
763
786
|
pass
|
|
764
787
|
|
|
765
788
|
# Also check old settings file
|
|
766
|
-
if self.old_settings_file.exists():
|
|
789
|
+
if self.old_settings_file is not None and self.old_settings_file.exists():
|
|
767
790
|
try:
|
|
768
791
|
with self.old_settings_file.open() as f:
|
|
769
792
|
old_settings = json.load(f)
|
|
@@ -6,18 +6,35 @@ including pre and post delegation hooks.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
|
+
import os
|
|
9
10
|
import sys
|
|
10
11
|
|
|
11
|
-
#
|
|
12
|
+
# Install-type-aware logging configuration BEFORE kuzu-memory imports
|
|
12
13
|
# This overrides kuzu-memory's WARNING-level basicConfig (fixes 1M-445)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
# but respects production install silence
|
|
15
|
+
try:
|
|
16
|
+
from claude_mpm.core.unified_paths import DeploymentContext, PathContext
|
|
17
|
+
|
|
18
|
+
context = PathContext.detect_deployment_context()
|
|
19
|
+
|
|
20
|
+
# Only configure verbose logging for development/editable installs
|
|
21
|
+
# Production installs remain silent by default
|
|
22
|
+
if context in (DeploymentContext.DEVELOPMENT, DeploymentContext.EDITABLE_INSTALL):
|
|
23
|
+
logging.basicConfig(
|
|
24
|
+
level=logging.INFO,
|
|
25
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
26
|
+
force=True, # Python 3.8+ - reconfigures root logger
|
|
27
|
+
stream=sys.stderr,
|
|
28
|
+
)
|
|
29
|
+
except ImportError:
|
|
30
|
+
# Fallback: if unified_paths not available, configure logging
|
|
31
|
+
# This maintains backward compatibility
|
|
32
|
+
logging.basicConfig(
|
|
33
|
+
level=logging.INFO,
|
|
34
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
35
|
+
force=True,
|
|
36
|
+
stream=sys.stderr,
|
|
37
|
+
)
|
|
21
38
|
from datetime import datetime, timezone
|
|
22
39
|
from typing import Optional
|
|
23
40
|
|
|
@@ -18,8 +18,7 @@ DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
|
|
|
18
18
|
|
|
19
19
|
# Response tracking integration
|
|
20
20
|
# NOTE: ResponseTracker import moved to _initialize_response_tracking() for lazy loading
|
|
21
|
-
# This prevents unnecessary import of
|
|
22
|
-
# when hooks don't need response tracking
|
|
21
|
+
# This prevents unnecessary import of heavy dependencies when hooks don't need response tracking
|
|
23
22
|
RESPONSE_TRACKING_AVAILABLE = (
|
|
24
23
|
True # Assume available, will check on actual initialization
|
|
25
24
|
)
|
|
@@ -51,7 +50,7 @@ class ResponseTrackingManager:
|
|
|
51
50
|
response tracking without code changes.
|
|
52
51
|
|
|
53
52
|
NOTE: ResponseTracker is imported lazily here to avoid loading
|
|
54
|
-
|
|
53
|
+
heavy dependencies unless actually needed.
|
|
55
54
|
"""
|
|
56
55
|
try:
|
|
57
56
|
# Lazy import of ResponseTracker to avoid unnecessary dependency loading
|
|
@@ -13,6 +13,7 @@ agent outputs because:
|
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
import re
|
|
16
|
+
from datetime import datetime
|
|
16
17
|
from typing import Dict, List
|
|
17
18
|
|
|
18
19
|
from claude_mpm.core.config import Config
|
|
@@ -47,6 +48,16 @@ except ImportError as e:
|
|
|
47
48
|
SOCKETIO_AVAILABLE = False
|
|
48
49
|
get_socketio_server = None
|
|
49
50
|
|
|
51
|
+
# Try to import event bus with fallback handling
|
|
52
|
+
try:
|
|
53
|
+
from claude_mpm.services.event_bus.event_bus import EventBus
|
|
54
|
+
|
|
55
|
+
EVENT_BUS_AVAILABLE = True
|
|
56
|
+
except ImportError as e:
|
|
57
|
+
logger.debug(f"EventBus not available: {e}")
|
|
58
|
+
EVENT_BUS_AVAILABLE = False
|
|
59
|
+
EventBus = None
|
|
60
|
+
|
|
50
61
|
|
|
51
62
|
class MemoryPreDelegationHook(PreDelegationHook):
|
|
52
63
|
"""Inject agent memory into delegation context.
|
|
@@ -83,6 +94,16 @@ class MemoryPreDelegationHook(PreDelegationHook):
|
|
|
83
94
|
logger.info("Memory manager not available - hook will be inactive")
|
|
84
95
|
self.memory_manager = None
|
|
85
96
|
|
|
97
|
+
# Initialize event bus for observability
|
|
98
|
+
if EVENT_BUS_AVAILABLE and EventBus:
|
|
99
|
+
try:
|
|
100
|
+
self.event_bus = EventBus.get_instance()
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logger.debug(f"Failed to get EventBus instance: {e}")
|
|
103
|
+
self.event_bus = None
|
|
104
|
+
else:
|
|
105
|
+
self.event_bus = None
|
|
106
|
+
|
|
86
107
|
def execute(self, context: HookContext) -> HookResult:
|
|
87
108
|
"""Add agent memory to delegation context.
|
|
88
109
|
|
|
@@ -137,7 +158,31 @@ INSTRUCTIONS: Review your memory above before proceeding. Apply learned patterns
|
|
|
137
158
|
|
|
138
159
|
logger.info(f"Injected memory for agent '{agent_id}'")
|
|
139
160
|
|
|
140
|
-
#
|
|
161
|
+
# Calculate memory size for observability
|
|
162
|
+
memory_size = len(memory_content)
|
|
163
|
+
|
|
164
|
+
# Emit event bus event for observability
|
|
165
|
+
if self.event_bus:
|
|
166
|
+
try:
|
|
167
|
+
# Determine memory source (project or user level)
|
|
168
|
+
# This is inferred from the memory manager's behavior
|
|
169
|
+
memory_source = (
|
|
170
|
+
"runtime" # Runtime loading from memory manager
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
self.event_bus.publish(
|
|
174
|
+
"agent.memory.loaded",
|
|
175
|
+
{
|
|
176
|
+
"agent_id": agent_id,
|
|
177
|
+
"memory_source": memory_source,
|
|
178
|
+
"memory_size": memory_size,
|
|
179
|
+
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
except Exception as event_error:
|
|
183
|
+
logger.debug(f"EventBus publish failed: {event_error}")
|
|
184
|
+
|
|
185
|
+
# Emit Socket.IO event for memory injected (legacy compatibility)
|
|
141
186
|
try:
|
|
142
187
|
socketio_server = get_socketio_server()
|
|
143
188
|
# Calculate size of injected content
|