claude-mpm 3.4.27__py3-none-any.whl → 3.5.0__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/INSTRUCTIONS.md +182 -299
- claude_mpm/agents/agent_loader.py +283 -57
- claude_mpm/agents/agent_loader_integration.py +6 -9
- claude_mpm/agents/base_agent.json +2 -1
- claude_mpm/agents/base_agent_loader.py +1 -1
- claude_mpm/cli/__init__.py +5 -7
- claude_mpm/cli/commands/__init__.py +0 -2
- claude_mpm/cli/commands/agents.py +1 -1
- claude_mpm/cli/commands/memory.py +1 -1
- claude_mpm/cli/commands/run.py +12 -0
- claude_mpm/cli/parser.py +0 -13
- claude_mpm/cli/utils.py +1 -1
- claude_mpm/config/__init__.py +44 -2
- claude_mpm/config/agent_config.py +348 -0
- claude_mpm/config/paths.py +322 -0
- claude_mpm/constants.py +0 -1
- claude_mpm/core/__init__.py +2 -5
- claude_mpm/core/agent_registry.py +63 -17
- claude_mpm/core/claude_runner.py +354 -43
- claude_mpm/core/config.py +7 -1
- claude_mpm/core/config_aliases.py +4 -3
- claude_mpm/core/config_paths.py +151 -0
- claude_mpm/core/factories.py +4 -50
- claude_mpm/core/logger.py +11 -13
- claude_mpm/core/service_registry.py +2 -2
- claude_mpm/dashboard/static/js/components/agent-inference.js +101 -25
- claude_mpm/dashboard/static/js/components/event-processor.js +3 -2
- claude_mpm/hooks/claude_hooks/hook_handler.py +343 -83
- claude_mpm/hooks/memory_integration_hook.py +1 -1
- claude_mpm/init.py +37 -6
- claude_mpm/scripts/socketio_daemon.py +6 -2
- claude_mpm/services/__init__.py +71 -3
- claude_mpm/services/agents/__init__.py +85 -0
- claude_mpm/services/agents/deployment/__init__.py +21 -0
- claude_mpm/services/{agent_deployment.py → agents/deployment/agent_deployment.py} +192 -41
- claude_mpm/services/{agent_lifecycle_manager.py → agents/deployment/agent_lifecycle_manager.py} +11 -10
- claude_mpm/services/agents/loading/__init__.py +11 -0
- claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +9 -8
- claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +2 -2
- claude_mpm/services/{framework_agent_loader.py → agents/loading/framework_agent_loader.py} +116 -40
- claude_mpm/services/agents/management/__init__.py +9 -0
- claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +6 -5
- claude_mpm/services/agents/memory/__init__.py +21 -0
- claude_mpm/services/{agent_memory_manager.py → agents/memory/agent_memory_manager.py} +3 -3
- claude_mpm/services/agents/registry/__init__.py +29 -0
- claude_mpm/services/{agent_registry.py → agents/registry/agent_registry.py} +101 -16
- claude_mpm/services/{deployed_agent_discovery.py → agents/registry/deployed_agent_discovery.py} +12 -2
- claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +6 -5
- claude_mpm/services/async_session_logger.py +584 -0
- claude_mpm/services/claude_session_logger.py +299 -0
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +2 -2
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +17 -17
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +3 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +1 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +1 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +19 -24
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +1 -1
- claude_mpm/services/framework_claude_md_generator.py +4 -2
- claude_mpm/services/memory/__init__.py +17 -0
- claude_mpm/services/{memory_builder.py → memory/builder.py} +3 -3
- claude_mpm/services/memory/cache/__init__.py +14 -0
- claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +1 -1
- claude_mpm/services/memory/cache/simple_cache.py +317 -0
- claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +1 -1
- claude_mpm/services/{memory_router.py → memory/router.py} +1 -1
- claude_mpm/services/optimized_hook_service.py +542 -0
- claude_mpm/services/project_registry.py +14 -8
- claude_mpm/services/response_tracker.py +237 -0
- claude_mpm/services/ticketing_service_original.py +4 -2
- claude_mpm/services/version_control/branch_strategy.py +3 -1
- claude_mpm/utils/paths.py +12 -10
- claude_mpm/utils/session_logging.py +114 -0
- claude_mpm/validation/agent_validator.py +2 -1
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/METADATA +26 -20
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/RECORD +83 -106
- claude_mpm/cli/commands/ui.py +0 -57
- claude_mpm/core/simple_runner.py +0 -1046
- claude_mpm/hooks/builtin/__init__.py +0 -1
- claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
- claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
- claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
- claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
- claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
- claude_mpm/orchestration/__init__.py +0 -6
- claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
- claude_mpm/orchestration/archive/factory.py +0 -215
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
- claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
- claude_mpm/orchestration/archive/orchestrator.py +0 -501
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
- claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
- claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
- claude_mpm/schemas/workflow_validator.py +0 -411
- claude_mpm/services/parent_directory_manager/__init__.py +0 -577
- claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
- claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
- claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
- claude_mpm/services/parent_directory_manager/operations.py +0 -186
- claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
- claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
- claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
- claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
- claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
- claude_mpm/ui/__init__.py +0 -1
- claude_mpm/ui/rich_terminal_ui.py +0 -295
- claude_mpm/ui/terminal_ui.py +0 -328
- /claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +0 -0
- /claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +0 -0
- /claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/WHEEL +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/top_level.txt +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Built-in hooks for claude-mpm."""
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Example logging hook for Claude MPM.
|
|
4
|
-
|
|
5
|
-
This hook demonstrates how to capture and log all prompts and responses
|
|
6
|
-
through the hook system, providing an alternative to built-in logging.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
import os
|
|
11
|
-
from datetime import datetime
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from typing import Dict, Any, Optional
|
|
14
|
-
|
|
15
|
-
# Configure your logging directory
|
|
16
|
-
LOG_DIR = Path.home() / ".claude-mpm-hook-logs"
|
|
17
|
-
LOG_DIR.mkdir(exist_ok=True)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def execute_pre_delegation_hook(agent: str, context: Dict[str, Any]) -> Dict[str, Any]:
|
|
21
|
-
"""
|
|
22
|
-
Log task details before delegation.
|
|
23
|
-
|
|
24
|
-
Args:
|
|
25
|
-
agent: The agent being invoked
|
|
26
|
-
context: Contains 'task' and other context data
|
|
27
|
-
|
|
28
|
-
Returns:
|
|
29
|
-
Empty dict (no modifications)
|
|
30
|
-
"""
|
|
31
|
-
timestamp = datetime.now().isoformat()
|
|
32
|
-
log_entry = {
|
|
33
|
-
"timestamp": timestamp,
|
|
34
|
-
"event": "pre_delegation",
|
|
35
|
-
"agent": agent,
|
|
36
|
-
"task": context.get("task", ""),
|
|
37
|
-
"context": context
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
# Write to daily log file
|
|
41
|
-
log_file = LOG_DIR / f"delegations_{datetime.now().strftime('%Y%m%d')}.jsonl"
|
|
42
|
-
with open(log_file, 'a') as f:
|
|
43
|
-
f.write(json.dumps(log_entry) + '\n')
|
|
44
|
-
|
|
45
|
-
return {}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def execute_post_delegation_hook(agent: str, result: Dict[str, Any]) -> Dict[str, Any]:
|
|
49
|
-
"""
|
|
50
|
-
Log complete prompt and response after delegation.
|
|
51
|
-
|
|
52
|
-
Args:
|
|
53
|
-
agent: The agent that was invoked
|
|
54
|
-
result: Contains task, response, execution_time, tokens
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
Empty dict (no modifications)
|
|
58
|
-
"""
|
|
59
|
-
timestamp = datetime.now().isoformat()
|
|
60
|
-
|
|
61
|
-
# Extract data
|
|
62
|
-
task = result.get("task", "")
|
|
63
|
-
response = result.get("response", "")
|
|
64
|
-
execution_time = result.get("execution_time", 0)
|
|
65
|
-
tokens = result.get("tokens", 0)
|
|
66
|
-
|
|
67
|
-
# Create detailed log entry
|
|
68
|
-
log_entry = {
|
|
69
|
-
"timestamp": timestamp,
|
|
70
|
-
"event": "post_delegation",
|
|
71
|
-
"agent": agent,
|
|
72
|
-
"task": task,
|
|
73
|
-
"response_length": len(response),
|
|
74
|
-
"execution_time": execution_time,
|
|
75
|
-
"tokens": tokens,
|
|
76
|
-
"success": not response.startswith("Error:"),
|
|
77
|
-
"response_preview": response[:500] + "..." if len(response) > 500 else response
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
# Write to agent-specific log
|
|
81
|
-
agent_log_dir = LOG_DIR / "agents" / agent.lower()
|
|
82
|
-
agent_log_dir.mkdir(parents=True, exist_ok=True)
|
|
83
|
-
|
|
84
|
-
log_file = agent_log_dir / f"{datetime.now().strftime('%Y%m%d')}.jsonl"
|
|
85
|
-
with open(log_file, 'a') as f:
|
|
86
|
-
f.write(json.dumps(log_entry) + '\n')
|
|
87
|
-
|
|
88
|
-
# Save full prompt/response if needed
|
|
89
|
-
if os.environ.get("CLAUDE_MPM_HOOK_LOG_FULL", "").lower() == "true":
|
|
90
|
-
# Create unique filename
|
|
91
|
-
task_hash = str(hash(task))[-8:]
|
|
92
|
-
prompt_file = agent_log_dir / f"prompt_{timestamp}_{task_hash}.txt"
|
|
93
|
-
response_file = agent_log_dir / f"response_{timestamp}_{task_hash}.txt"
|
|
94
|
-
|
|
95
|
-
# Note: We don't have access to the original prompt in post-delegation
|
|
96
|
-
# To capture prompts, you'd need to store them in pre-delegation
|
|
97
|
-
# and match them up using task hash or similar
|
|
98
|
-
|
|
99
|
-
response_file.write_text(response)
|
|
100
|
-
|
|
101
|
-
log_entry["response_file"] = str(response_file)
|
|
102
|
-
|
|
103
|
-
return {}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def execute_submit_hook(prompt: str, session_type: str) -> Dict[str, Any]:
|
|
107
|
-
"""
|
|
108
|
-
Log user prompts at session start.
|
|
109
|
-
|
|
110
|
-
Args:
|
|
111
|
-
prompt: The user's input prompt
|
|
112
|
-
session_type: Type of session (e.g., "subprocess")
|
|
113
|
-
|
|
114
|
-
Returns:
|
|
115
|
-
Empty dict (no modifications)
|
|
116
|
-
"""
|
|
117
|
-
timestamp = datetime.now().isoformat()
|
|
118
|
-
log_entry = {
|
|
119
|
-
"timestamp": timestamp,
|
|
120
|
-
"event": "user_submit",
|
|
121
|
-
"session_type": session_type,
|
|
122
|
-
"prompt": prompt,
|
|
123
|
-
"prompt_length": len(prompt)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
# Write to session log
|
|
127
|
-
log_file = LOG_DIR / f"sessions_{datetime.now().strftime('%Y%m%d')}.jsonl"
|
|
128
|
-
with open(log_file, 'a') as f:
|
|
129
|
-
f.write(json.dumps(log_entry) + '\n')
|
|
130
|
-
|
|
131
|
-
print(f"[Logging Hook] Logged user prompt to {log_file}")
|
|
132
|
-
|
|
133
|
-
return {}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
# Optional: Hook metadata for registration
|
|
137
|
-
HOOK_METADATA = {
|
|
138
|
-
"name": "logging_hook",
|
|
139
|
-
"description": "Comprehensive logging of all prompts and responses",
|
|
140
|
-
"version": "1.0.0",
|
|
141
|
-
"author": "claude-mpm",
|
|
142
|
-
"events": ["pre_delegation", "post_delegation", "submit"],
|
|
143
|
-
"config": {
|
|
144
|
-
"log_dir": str(LOG_DIR),
|
|
145
|
-
"full_logging": os.environ.get("CLAUDE_MPM_HOOK_LOG_FULL", "false")
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if __name__ == "__main__":
|
|
151
|
-
# Test the hook
|
|
152
|
-
print(f"Logging hook configured to write to: {LOG_DIR}")
|
|
153
|
-
print("Set CLAUDE_MPM_HOOK_LOG_FULL=true to save complete responses")
|
|
154
|
-
|
|
155
|
-
# Example usage
|
|
156
|
-
execute_submit_hook("Test prompt", "test")
|
|
157
|
-
execute_pre_delegation_hook("Engineer", {"task": "Test task"})
|
|
158
|
-
execute_post_delegation_hook("Engineer", {
|
|
159
|
-
"task": "Test task",
|
|
160
|
-
"response": "Test response",
|
|
161
|
-
"execution_time": 1.5,
|
|
162
|
-
"tokens": 100
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
print(f"\nCheck logs in: {LOG_DIR}")
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
"""Example of how to register memory integration hooks.
|
|
2
|
-
|
|
3
|
-
WHY: This demonstrates how to register the memory hooks with the HookService
|
|
4
|
-
for automatic memory injection and learning extraction.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from claude_mpm.hooks.memory_integration_hook import (
|
|
8
|
-
MemoryPreDelegationHook,
|
|
9
|
-
MemoryPostDelegationHook
|
|
10
|
-
)
|
|
11
|
-
from claude_mpm.services.hook_service import HookService
|
|
12
|
-
from claude_mpm.core.config import Config
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def register_memory_hooks(hook_service: HookService, config: Config = None):
|
|
16
|
-
"""Register memory integration hooks with the hook service.
|
|
17
|
-
|
|
18
|
-
WHY: To enable automatic memory management, both hooks need to be
|
|
19
|
-
registered with appropriate priorities:
|
|
20
|
-
- Pre-hook runs early (priority 20) to inject memory into context
|
|
21
|
-
- Post-hook runs late (priority 80) to extract learnings after processing
|
|
22
|
-
|
|
23
|
-
Args:
|
|
24
|
-
hook_service: The HookService instance to register with
|
|
25
|
-
config: Optional configuration (will create default if not provided)
|
|
26
|
-
"""
|
|
27
|
-
config = config or Config()
|
|
28
|
-
|
|
29
|
-
# Only register if memory system is enabled
|
|
30
|
-
if not config.get('memory.enabled', True):
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
# Register pre-delegation hook for memory injection
|
|
34
|
-
pre_hook = MemoryPreDelegationHook(config)
|
|
35
|
-
hook_service.register_hook(pre_hook)
|
|
36
|
-
|
|
37
|
-
# Register post-delegation hook for learning extraction
|
|
38
|
-
# Only if auto-learning is enabled
|
|
39
|
-
if config.get('memory.auto_learning', False):
|
|
40
|
-
post_hook = MemoryPostDelegationHook(config)
|
|
41
|
-
hook_service.register_hook(post_hook)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# Example usage:
|
|
45
|
-
if __name__ == "__main__":
|
|
46
|
-
# This would typically be done during application initialization
|
|
47
|
-
config = Config(config={
|
|
48
|
-
'memory': {
|
|
49
|
-
'enabled': True,
|
|
50
|
-
'auto_learning': True,
|
|
51
|
-
'limits': {
|
|
52
|
-
'default_size_kb': 8,
|
|
53
|
-
'max_items_per_section': 20
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
# Create hook service (normally this would be passed from main app)
|
|
59
|
-
from claude_mpm.services.hook_service import HookService
|
|
60
|
-
hook_service = HookService(config)
|
|
61
|
-
|
|
62
|
-
# Register memory hooks
|
|
63
|
-
register_memory_hooks(hook_service, config)
|
|
64
|
-
|
|
65
|
-
print("Memory hooks registered successfully!")
|
|
66
|
-
print(f"Pre-delegation hook: {hook_service.get_hooks('pre_delegation')}")
|
|
67
|
-
print(f"Post-delegation hook: {hook_service.get_hooks('post_delegation')}")
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
"""Hook to intercept and handle /mpm: commands."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import subprocess
|
|
5
|
-
import sys
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
from claude_mpm.hooks.base_hook import SubmitHook, HookContext, HookResult
|
|
9
|
-
from claude_mpm.core.logger import get_logger
|
|
10
|
-
|
|
11
|
-
logger = get_logger(__name__)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class MpmCommandHook(SubmitHook):
|
|
15
|
-
"""Hook that intercepts /mpm commands and routes them to the command router."""
|
|
16
|
-
|
|
17
|
-
def __init__(self):
|
|
18
|
-
super().__init__(name="mpm_command", priority=1) # High priority to intercept early
|
|
19
|
-
self.command_prefix = "/mpm "
|
|
20
|
-
self.command_router_path = self._find_command_router()
|
|
21
|
-
|
|
22
|
-
def _find_command_router(self) -> Path:
|
|
23
|
-
"""Find the command router script."""
|
|
24
|
-
# Look for command router relative to project root
|
|
25
|
-
possible_paths = [
|
|
26
|
-
Path(".claude/scripts/command_router.py"),
|
|
27
|
-
Path(__file__).parent.parent.parent.parent.parent / ".claude/scripts/command_router.py"
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
for path in possible_paths:
|
|
31
|
-
if path.exists():
|
|
32
|
-
return path.resolve()
|
|
33
|
-
|
|
34
|
-
# Default path
|
|
35
|
-
return Path(".claude/scripts/command_router.py").resolve()
|
|
36
|
-
|
|
37
|
-
def execute(self, context: HookContext) -> HookResult:
|
|
38
|
-
"""Check for /mpm commands and execute them directly."""
|
|
39
|
-
try:
|
|
40
|
-
prompt = context.data.get('prompt', '').strip()
|
|
41
|
-
|
|
42
|
-
# Check if this is an /mpm command
|
|
43
|
-
if not prompt.startswith(self.command_prefix):
|
|
44
|
-
# Not our command, pass through
|
|
45
|
-
return HookResult(
|
|
46
|
-
success=True,
|
|
47
|
-
data=context.data,
|
|
48
|
-
modified=False
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
# Extract command and arguments
|
|
52
|
-
command_line = prompt[len(self.command_prefix):].strip()
|
|
53
|
-
parts = command_line.split()
|
|
54
|
-
|
|
55
|
-
if not parts:
|
|
56
|
-
return HookResult(
|
|
57
|
-
success=True,
|
|
58
|
-
data={
|
|
59
|
-
'prompt': '',
|
|
60
|
-
'response': "No command specified. Available commands: test",
|
|
61
|
-
'skip_llm': True
|
|
62
|
-
},
|
|
63
|
-
modified=True,
|
|
64
|
-
metadata={'command_handled': True}
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
command = parts[0]
|
|
68
|
-
args = parts[1:]
|
|
69
|
-
|
|
70
|
-
logger.info(f"Executing /mpm {command} with args: {args}")
|
|
71
|
-
|
|
72
|
-
# Execute command using command router
|
|
73
|
-
try:
|
|
74
|
-
# Run the command router script
|
|
75
|
-
cmd = [sys.executable, str(self.command_router_path), command] + args
|
|
76
|
-
result = subprocess.run(
|
|
77
|
-
cmd,
|
|
78
|
-
capture_output=True,
|
|
79
|
-
text=True,
|
|
80
|
-
check=False
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
if result.returncode == 0:
|
|
84
|
-
response = result.stdout.strip()
|
|
85
|
-
else:
|
|
86
|
-
response = f"Command failed: {result.stderr.strip() or 'Unknown error'}"
|
|
87
|
-
|
|
88
|
-
logger.info(f"Command result: {response}")
|
|
89
|
-
|
|
90
|
-
# Return result without going to LLM
|
|
91
|
-
return HookResult(
|
|
92
|
-
success=True,
|
|
93
|
-
data={
|
|
94
|
-
'prompt': '', # Clear prompt to prevent LLM processing
|
|
95
|
-
'response': response,
|
|
96
|
-
'skip_llm': True # Flag to skip LLM
|
|
97
|
-
},
|
|
98
|
-
modified=True,
|
|
99
|
-
metadata={
|
|
100
|
-
'command_handled': True,
|
|
101
|
-
'command': command,
|
|
102
|
-
'args': args
|
|
103
|
-
}
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
except Exception as e:
|
|
107
|
-
logger.error(f"Failed to execute command: {e}")
|
|
108
|
-
return HookResult(
|
|
109
|
-
success=True,
|
|
110
|
-
data={
|
|
111
|
-
'prompt': '',
|
|
112
|
-
'response': f"Error executing command: {str(e)}",
|
|
113
|
-
'skip_llm': True
|
|
114
|
-
},
|
|
115
|
-
modified=True,
|
|
116
|
-
metadata={'command_error': str(e)}
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
except Exception as e:
|
|
120
|
-
logger.error(f"MPM command hook failed: {e}")
|
|
121
|
-
# On error, pass through to normal processing
|
|
122
|
-
return HookResult(
|
|
123
|
-
success=False,
|
|
124
|
-
error=str(e)
|
|
125
|
-
)
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
"""Example post-delegation hook implementation."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import re
|
|
5
|
-
from typing import Dict, Any, List
|
|
6
|
-
|
|
7
|
-
from claude_mpm.hooks.base_hook import PostDelegationHook, HookContext, HookResult
|
|
8
|
-
from claude_mpm.core.logger import get_logger
|
|
9
|
-
|
|
10
|
-
logger = get_logger(__name__)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class ResultValidatorHook(PostDelegationHook):
|
|
14
|
-
"""Hook that validates agent results for quality and completeness."""
|
|
15
|
-
|
|
16
|
-
def __init__(self):
|
|
17
|
-
super().__init__(name="result_validator", priority=10)
|
|
18
|
-
|
|
19
|
-
def execute(self, context: HookContext) -> HookResult:
|
|
20
|
-
"""Validate agent results."""
|
|
21
|
-
try:
|
|
22
|
-
result = context.data.get('result', {})
|
|
23
|
-
agent = context.data.get('agent', 'unknown')
|
|
24
|
-
|
|
25
|
-
# Validation checks
|
|
26
|
-
issues = []
|
|
27
|
-
|
|
28
|
-
# Check for empty results
|
|
29
|
-
if not result:
|
|
30
|
-
issues.append("Empty result returned")
|
|
31
|
-
|
|
32
|
-
# Check for error indicators
|
|
33
|
-
error_patterns = ['error', 'failed', 'exception', 'traceback']
|
|
34
|
-
result_str = json.dumps(result).lower()
|
|
35
|
-
for pattern in error_patterns:
|
|
36
|
-
if pattern in result_str and 'success' not in result:
|
|
37
|
-
issues.append(f"Result contains '{pattern}' indicator")
|
|
38
|
-
|
|
39
|
-
# Agent-specific validation
|
|
40
|
-
if agent.lower() == 'engineer' and 'code' in str(result):
|
|
41
|
-
# Check for code quality indicators
|
|
42
|
-
if 'todo' in result_str or 'fixme' in result_str:
|
|
43
|
-
issues.append("Code contains TODO/FIXME comments")
|
|
44
|
-
|
|
45
|
-
if issues:
|
|
46
|
-
logger.warning(f"Validation issues found: {issues}")
|
|
47
|
-
return HookResult(
|
|
48
|
-
success=True,
|
|
49
|
-
data={
|
|
50
|
-
'result': result,
|
|
51
|
-
'validation_issues': issues
|
|
52
|
-
},
|
|
53
|
-
modified=True,
|
|
54
|
-
metadata={'issues_count': len(issues)}
|
|
55
|
-
)
|
|
56
|
-
else:
|
|
57
|
-
return HookResult(
|
|
58
|
-
success=True,
|
|
59
|
-
data=context.data,
|
|
60
|
-
modified=False,
|
|
61
|
-
metadata={'validated': True}
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
except Exception as e:
|
|
65
|
-
logger.error(f"Result validation failed: {e}")
|
|
66
|
-
return HookResult(
|
|
67
|
-
success=False,
|
|
68
|
-
error=str(e)
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class ResultMetricsHook(PostDelegationHook):
|
|
73
|
-
"""Hook that collects metrics from agent results."""
|
|
74
|
-
|
|
75
|
-
def __init__(self):
|
|
76
|
-
super().__init__(name="result_metrics", priority=50)
|
|
77
|
-
|
|
78
|
-
def execute(self, context: HookContext) -> HookResult:
|
|
79
|
-
"""Collect metrics from agent results."""
|
|
80
|
-
try:
|
|
81
|
-
result = context.data.get('result', {})
|
|
82
|
-
agent = context.data.get('agent', 'unknown')
|
|
83
|
-
execution_time = context.metadata.get('execution_time_ms', 0)
|
|
84
|
-
|
|
85
|
-
# Collect metrics
|
|
86
|
-
metrics = {
|
|
87
|
-
'agent': agent,
|
|
88
|
-
'execution_time_ms': execution_time,
|
|
89
|
-
'result_size_bytes': len(json.dumps(result).encode()),
|
|
90
|
-
'timestamp': context.timestamp.isoformat()
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
# Agent-specific metrics
|
|
94
|
-
if agent.lower() == 'engineer':
|
|
95
|
-
# Count code-related metrics
|
|
96
|
-
code_content = str(result)
|
|
97
|
-
metrics['lines_of_code'] = code_content.count('\n')
|
|
98
|
-
metrics['functions_created'] = len(re.findall(r'def\s+\w+', code_content))
|
|
99
|
-
metrics['classes_created'] = len(re.findall(r'class\s+\w+', code_content))
|
|
100
|
-
|
|
101
|
-
elif agent.lower() == 'qa':
|
|
102
|
-
# Count test-related metrics
|
|
103
|
-
test_content = str(result)
|
|
104
|
-
metrics['tests_count'] = len(re.findall(r'test_\w+', test_content))
|
|
105
|
-
metrics['assertions_count'] = len(re.findall(r'assert\s+', test_content))
|
|
106
|
-
|
|
107
|
-
logger.info(f"Collected metrics: {metrics}")
|
|
108
|
-
|
|
109
|
-
return HookResult(
|
|
110
|
-
success=True,
|
|
111
|
-
data={
|
|
112
|
-
'result': result,
|
|
113
|
-
'metrics': metrics
|
|
114
|
-
},
|
|
115
|
-
modified=True,
|
|
116
|
-
metadata=metrics
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
except Exception as e:
|
|
120
|
-
logger.error(f"Metrics collection failed: {e}")
|
|
121
|
-
return HookResult(
|
|
122
|
-
success=False,
|
|
123
|
-
error=str(e)
|
|
124
|
-
)
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
"""Example pre-delegation hook implementation."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from typing import Dict, Any, List
|
|
5
|
-
|
|
6
|
-
from claude_mpm.hooks.base_hook import PreDelegationHook, HookContext, HookResult
|
|
7
|
-
from claude_mpm.core.logger import get_logger
|
|
8
|
-
|
|
9
|
-
logger = get_logger(__name__)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class ContextFilterHook(PreDelegationHook):
|
|
13
|
-
"""Hook that filters sensitive information from context before delegation."""
|
|
14
|
-
|
|
15
|
-
def __init__(self):
|
|
16
|
-
super().__init__(name="context_filter", priority=10)
|
|
17
|
-
self.sensitive_keys = {
|
|
18
|
-
'api_key', 'secret', 'password', 'token',
|
|
19
|
-
'private_key', 'credentials', 'auth'
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
def execute(self, context: HookContext) -> HookResult:
|
|
23
|
-
"""Filter sensitive information from delegation context."""
|
|
24
|
-
try:
|
|
25
|
-
agent_context = context.data.get('context', {})
|
|
26
|
-
filtered_context = self._filter_sensitive(agent_context)
|
|
27
|
-
|
|
28
|
-
if filtered_context != agent_context:
|
|
29
|
-
logger.info("Filtered sensitive information from context")
|
|
30
|
-
return HookResult(
|
|
31
|
-
success=True,
|
|
32
|
-
data={
|
|
33
|
-
'agent': context.data.get('agent'),
|
|
34
|
-
'context': filtered_context
|
|
35
|
-
},
|
|
36
|
-
modified=True,
|
|
37
|
-
metadata={'filtered_keys': True}
|
|
38
|
-
)
|
|
39
|
-
else:
|
|
40
|
-
return HookResult(
|
|
41
|
-
success=True,
|
|
42
|
-
data=context.data,
|
|
43
|
-
modified=False
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
except Exception as e:
|
|
47
|
-
logger.error(f"Context filtering failed: {e}")
|
|
48
|
-
return HookResult(
|
|
49
|
-
success=False,
|
|
50
|
-
error=str(e)
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
def _filter_sensitive(self, data: Any) -> Any:
|
|
54
|
-
"""Recursively filter sensitive keys from data."""
|
|
55
|
-
if isinstance(data, dict):
|
|
56
|
-
filtered = {}
|
|
57
|
-
for key, value in data.items():
|
|
58
|
-
if any(sensitive in key.lower() for sensitive in self.sensitive_keys):
|
|
59
|
-
filtered[key] = "[REDACTED]"
|
|
60
|
-
else:
|
|
61
|
-
filtered[key] = self._filter_sensitive(value)
|
|
62
|
-
return filtered
|
|
63
|
-
elif isinstance(data, list):
|
|
64
|
-
return [self._filter_sensitive(item) for item in data]
|
|
65
|
-
else:
|
|
66
|
-
return data
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
class AgentCapabilityEnhancerHook(PreDelegationHook):
|
|
70
|
-
"""Hook that enhances agent context with additional capabilities."""
|
|
71
|
-
|
|
72
|
-
def __init__(self):
|
|
73
|
-
super().__init__(name="capability_enhancer", priority=30)
|
|
74
|
-
self.agent_enhancements = {
|
|
75
|
-
'engineer': {
|
|
76
|
-
'tools': ['code_analysis', 'refactoring', 'testing'],
|
|
77
|
-
'context': 'You have access to advanced code analysis tools.'
|
|
78
|
-
},
|
|
79
|
-
'researcher': {
|
|
80
|
-
'tools': ['web_search', 'document_analysis'],
|
|
81
|
-
'context': 'You can search the web and analyze documents.'
|
|
82
|
-
},
|
|
83
|
-
'qa': {
|
|
84
|
-
'tools': ['test_runner', 'coverage_analyzer'],
|
|
85
|
-
'context': 'You have access to comprehensive testing tools.'
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
def execute(self, context: HookContext) -> HookResult:
|
|
90
|
-
"""Enhance agent capabilities based on agent type."""
|
|
91
|
-
try:
|
|
92
|
-
agent_type = context.data.get('agent', '').lower()
|
|
93
|
-
|
|
94
|
-
if agent_type in self.agent_enhancements:
|
|
95
|
-
enhancement = self.agent_enhancements[agent_type]
|
|
96
|
-
|
|
97
|
-
# Add enhancements to context
|
|
98
|
-
enhanced_context = context.data.get('context', {}).copy()
|
|
99
|
-
enhanced_context['additional_tools'] = enhancement['tools']
|
|
100
|
-
enhanced_context['enhanced_context'] = enhancement['context']
|
|
101
|
-
|
|
102
|
-
logger.info(f"Enhanced {agent_type} agent with additional capabilities")
|
|
103
|
-
|
|
104
|
-
return HookResult(
|
|
105
|
-
success=True,
|
|
106
|
-
data={
|
|
107
|
-
'agent': context.data.get('agent'),
|
|
108
|
-
'context': enhanced_context
|
|
109
|
-
},
|
|
110
|
-
modified=True,
|
|
111
|
-
metadata={'enhancements_applied': True}
|
|
112
|
-
)
|
|
113
|
-
else:
|
|
114
|
-
return HookResult(
|
|
115
|
-
success=True,
|
|
116
|
-
data=context.data,
|
|
117
|
-
modified=False
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
except Exception as e:
|
|
121
|
-
logger.error(f"Capability enhancement failed: {e}")
|
|
122
|
-
return HookResult(
|
|
123
|
-
success=False,
|
|
124
|
-
error=str(e)
|
|
125
|
-
)
|