claude-mpm 0.3.0__py3-none-any.whl → 1.1.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.
Files changed (42) hide show
  1. claude_mpm/_version.py +3 -2
  2. claude_mpm/agents/INSTRUCTIONS.md +23 -0
  3. claude_mpm/agents/__init__.py +2 -2
  4. claude_mpm/agents/agent-template.yaml +83 -0
  5. claude_mpm/agents/agent_loader.py +66 -90
  6. claude_mpm/agents/base_agent_loader.py +10 -15
  7. claude_mpm/cli.py +41 -47
  8. claude_mpm/cli_enhancements.py +297 -0
  9. claude_mpm/core/agent_name_normalizer.py +49 -0
  10. claude_mpm/core/factories.py +1 -46
  11. claude_mpm/core/service_registry.py +0 -8
  12. claude_mpm/core/simple_runner.py +50 -0
  13. claude_mpm/generators/__init__.py +5 -0
  14. claude_mpm/generators/agent_profile_generator.py +137 -0
  15. claude_mpm/hooks/README.md +75 -221
  16. claude_mpm/hooks/builtin/mpm_command_hook.py +125 -0
  17. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +8 -7
  18. claude_mpm/hooks/claude_hooks/__init__.py +5 -0
  19. claude_mpm/hooks/claude_hooks/hook_handler.py +399 -0
  20. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +47 -0
  21. claude_mpm/hooks/validation_hooks.py +181 -0
  22. claude_mpm/services/agent_management_service.py +4 -4
  23. claude_mpm/services/agent_profile_loader.py +1 -1
  24. claude_mpm/services/agent_registry.py +0 -1
  25. claude_mpm/services/base_agent_manager.py +3 -3
  26. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +57 -31
  27. claude_mpm/utils/error_handler.py +247 -0
  28. claude_mpm/validation/__init__.py +5 -0
  29. claude_mpm/validation/agent_validator.py +175 -0
  30. {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/METADATA +44 -7
  31. {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/RECORD +34 -30
  32. claude_mpm/config/hook_config.py +0 -42
  33. claude_mpm/hooks/hook_client.py +0 -264
  34. claude_mpm/hooks/hook_runner.py +0 -370
  35. claude_mpm/hooks/json_rpc_executor.py +0 -259
  36. claude_mpm/hooks/json_rpc_hook_client.py +0 -319
  37. claude_mpm/services/hook_service.py +0 -388
  38. claude_mpm/services/hook_service_manager.py +0 -223
  39. claude_mpm/services/json_rpc_hook_manager.py +0 -92
  40. {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/WHEEL +0 -0
  41. {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/entry_points.txt +0 -0
  42. {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/top_level.txt +0 -0
@@ -1,243 +1,97 @@
1
- # Claude MPM Hook System
1
+ # Claude Code Hooks System
2
2
 
3
- The Claude MPM hook system provides extensibility points for customizing behavior at key stages of execution.
4
-
5
- ## Current Implementation (v0.5.0+)
6
-
7
- As of version 0.5.0, the hook system uses JSON-RPC for all hook executions. The previous HTTP-based implementation is deprecated and will be removed in a future release.
3
+ This directory contains the Claude Code hook integration for claude-mpm.
8
4
 
9
5
  ## Overview
10
6
 
11
- The hook system allows you to intercept and modify behavior at various points in the orchestration workflow:
12
-
13
- - **Submit Hooks**: Process user prompts before orchestration
14
- - **Pre-Delegation Hooks**: Filter/enhance context before delegating to agents
15
- - **Post-Delegation Hooks**: Validate/process results from agents
16
- - **Ticket Extraction Hooks**: Automatically extract and create tickets from conversations
7
+ The hook system allows claude-mpm to intercept and handle commands typed in Claude Code, particularly the `/mpm` commands.
17
8
 
18
- ## Architecture
9
+ ## Structure
19
10
 
20
11
  ```
21
- ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
22
- │ Orchestrator │────▶│ JSON-RPC Client │────▶│ Hook Types │
23
- │ │ │ ├─────────────────┤
24
- - Process prompt│ │ - No server req. │ │ - SubmitHook │
25
- - Delegate work │ │ - Direct exec │ │ - PreDelegation │
26
- │ - Create tickets│ │ - Auto discovery │ │ - PostDelegation│
27
- └─────────────────┘ └──────────────────┘ │ - TicketExtract │
28
- └─────────────────┘
12
+ hooks/
13
+ ├── claude_hooks/ # Claude Code hook implementation
14
+ ├── hook_handler.py # Main Python handler that processes events
15
+ └── hook_wrapper.sh # Shell wrapper script (this is what gets installed in ~/.claude/settings.json)
16
+ └── builtin/ # Legacy internal hooks (deprecated)
29
17
  ```
30
18
 
31
- ## Usage
19
+ ## Claude Code Hooks
32
20
 
33
- ### Basic Client Usage
34
- ```python
35
- from claude_mpm.hooks.json_rpc_hook_client import JSONRPCHookClient
21
+ The Claude Code hooks are the primary integration point between claude-mpm and Claude Code. They allow:
36
22
 
37
- # Create client
38
- client = JSONRPCHookClient()
23
+ - Intercepting `/mpm` commands before they reach the LLM
24
+ - Providing custom responses and actions
25
+ - Blocking LLM processing when appropriate
39
26
 
40
- # Check system health
41
- health = client.health_check()
42
- print(f"Status: {health['status']}")
43
- print(f"Hooks available: {health['hook_count']}")
27
+ ### Installation
44
28
 
45
- # List available hooks
46
- hooks = client.list_hooks()
47
- for hook_type, hook_list in hooks.items():
48
- print(f"{hook_type}: {len(hook_list)} hooks")
49
- ```
29
+ To install the Claude Code hooks:
50
30
 
51
- ### Executing Hooks
52
- ```python
53
- # Execute submit hooks
54
- results = client.execute_submit_hook(
55
- prompt="URGENT: Fix the login bug",
56
- user_id="user123"
57
- )
58
-
59
- # Get modified data
60
- modified_data = client.get_modified_data(results)
61
- if modified_data.get('priority') == 'high':
62
- print("High priority task detected!")
63
-
64
- # Execute pre-delegation hooks
65
- results = client.execute_pre_delegation_hook(
66
- agent="engineer",
67
- context={"task": "implement feature"}
68
- )
69
-
70
- # Execute ticket extraction
71
- results = client.execute_ticket_extraction_hook(
72
- content="TODO: Add tests\nFIXME: Memory leak"
73
- )
74
- tickets = client.get_extracted_tickets(results)
31
+ ```bash
32
+ python scripts/install_hooks.py
75
33
  ```
76
34
 
77
- ## Hook Types
78
-
79
- ### 1. Submit Hooks
80
- Process user prompts before they're sent to the orchestrator:
81
- ```python
82
- from claude_mpm.hooks.base_hook import SubmitHook, HookContext, HookResult
83
-
84
- class TicketDetectionSubmitHook(SubmitHook):
85
- name = "ticket_detection"
86
- priority = 10
87
-
88
- def execute(self, context: HookContext) -> HookResult:
89
- prompt = context.data.get('prompt', '')
90
- # Detect ticket references like TSK-001
91
- tickets = self.ticket_pattern.findall(prompt)
92
- return HookResult(
93
- success=True,
94
- data={'tickets': tickets},
95
- modified=True
96
- )
97
- ```
35
+ This will:
36
+ 1. Create/update `~/.claude/settings.json` with hook configuration
37
+ 2. Point to the `hook_wrapper.sh` script
38
+ 3. Copy any custom commands to `~/.claude/commands/`
98
39
 
99
- ### 2. Pre-Delegation Hooks
100
- Modify agent context before delegation:
101
- ```python
102
- from claude_mpm.hooks.base_hook import PreDelegationHook
103
-
104
- class ContextFilterHook(PreDelegationHook):
105
- name = "context_filter"
106
- priority = 20
107
-
108
- def execute(self, context: HookContext) -> HookResult:
109
- # Filter sensitive information
110
- filtered_context = self._filter_sensitive(context.data['context'])
111
- return HookResult(
112
- success=True,
113
- data={'context': filtered_context},
114
- modified=True
115
- )
116
- ```
40
+ ### How It Works
117
41
 
118
- ### 3. Post-Delegation Hooks
119
- Process agent results:
120
- ```python
121
- from claude_mpm.hooks.base_hook import PostDelegationHook
122
-
123
- class ResultValidatorHook(PostDelegationHook):
124
- name = "result_validator"
125
- priority = 30
126
-
127
- def execute(self, context: HookContext) -> HookResult:
128
- result = context.data.get('result', {})
129
- # Validate result quality
130
- issues = self._validate_result(result)
131
- return HookResult(
132
- success=True,
133
- data={'validation_issues': issues},
134
- modified=bool(issues)
135
- )
136
- ```
42
+ 1. When you type in Claude Code, it triggers hook events
43
+ 2. Claude Code calls `hook_wrapper.sh` (the path in `~/.claude/settings.json`)
44
+ 3. The wrapper script:
45
+ - Detects if it's running from a local dev environment, npm, or PyPI installation
46
+ - Activates the appropriate Python environment
47
+ - Runs `hook_handler.py` with the event data
48
+ 4. The handler processes various event types:
49
+ - **UserPromptSubmit**: Checks if the prompt starts with `/mpm` and handles commands
50
+ - **PreToolUse**: Logs tool usage before execution
51
+ - **PostToolUse**: Logs tool results after execution
52
+ - **Stop**: Logs when a session or task stops
53
+ - **SubagentStop**: Logs when a subagent completes with agent type and ID
54
+ 5. For `/mpm` commands, it returns exit code 2 to block LLM processing
55
+ 6. All events are logged to project-specific log files in `.claude-mpm/logs/`
137
56
 
138
- ### 4. Ticket Extraction Hooks
139
- Extract actionable items from conversations:
140
- ```python
141
- from claude_mpm.hooks.base_hook import TicketExtractionHook
142
-
143
- class AutoTicketExtractionHook(TicketExtractionHook):
144
- name = "auto_ticket_extraction"
145
- priority = 40
146
-
147
- def execute(self, context: HookContext) -> HookResult:
148
- content = context.data.get('content', '')
149
- # Extract TODO, FIXME, etc.
150
- tickets = self._extract_tickets(content)
151
- return HookResult(
152
- success=True,
153
- data={'tickets': tickets},
154
- modified=True
155
- )
156
- ```
57
+ ### Available Commands
58
+
59
+ - `/mpm` - Show help and available commands
60
+ - `/mpm status` - Show claude-mpm status and environment
61
+ - `/mpm help` - Show detailed help
62
+
63
+ ### Debugging
64
+
65
+ To enable debug logging for hooks:
157
66
 
158
- ## Creating Custom Hooks
159
-
160
- ### 1. Create Hook File
161
- Create a new Python file in the `builtin/` directory:
162
-
163
- ```python
164
- # builtin/my_custom_hook.py
165
- from claude_mpm.hooks.base_hook import SubmitHook, HookContext, HookResult
166
-
167
- class MyCustomHook(SubmitHook):
168
- name = "my_custom_hook"
169
- priority = 25 # 0-100, lower executes first
170
-
171
- def execute(self, context: HookContext) -> HookResult:
172
- # Your hook logic here
173
- prompt = context.data.get('prompt', '')
174
-
175
- # Process prompt
176
- processed = self._process(prompt)
177
-
178
- return HookResult(
179
- success=True,
180
- data={'prompt': processed},
181
- modified=True
182
- )
183
-
184
- def _process(self, prompt: str) -> str:
185
- # Your processing logic
186
- return prompt.upper()
67
+ ```bash
68
+ export CLAUDE_MPM_LOG_LEVEL=DEBUG
187
69
  ```
188
70
 
189
- ### 2. Hook Discovery
190
- Hooks are automatically discovered from the `builtin/` directory when the client is initialized. No manual registration required!
191
-
192
- ### 3. Hook Priority
193
- Hooks execute in priority order (0-100, lower first):
194
- - 0-20: Critical preprocessing (security, validation)
195
- - 21-40: Data transformation
196
- - 41-60: Enhancement and enrichment
197
- - 61-80: Analytics and metrics
198
- - 81-100: Low priority post-processing
199
-
200
- ## Migration from HTTP-based Hooks
201
-
202
- If you're migrating from the old HTTP-based hook system, see `/docs/hook_system_migration_guide.md` for detailed instructions.
203
-
204
- Key changes:
205
- - No server startup required
206
- - Import from `json_rpc_hook_client` instead of `hook_client`
207
- - Automatic hook discovery from `builtin/` directory
208
- - Better error handling and performance
209
-
210
- ## Best Practices
211
-
212
- 1. **Keep Hooks Fast**: Hooks run synchronously, so keep execution time minimal
213
- 2. **Handle Errors Gracefully**: Always return a HookResult, even on failure
214
- 3. **Use Appropriate Priority**: Consider hook dependencies when setting priority
215
- 4. **Validate Input**: Always validate context data before processing
216
- 5. **Log Important Events**: Use logging for debugging and monitoring
217
- 6. **Make Hooks Idempotent**: Hooks should produce same result if run multiple times
218
-
219
- ## Troubleshooting
220
-
221
- ### Hooks Not Discovered
222
- - Verify hook file is in `builtin/` directory
223
- - Check file has `.py` extension
224
- - Ensure hook class inherits from correct base type
225
- - Check for Python syntax errors
226
-
227
- ### Hook Execution Errors
228
- - Enable debug logging to see detailed errors
229
- - Check hook's execute method returns HookResult
230
- - Verify context data structure matches expectations
231
-
232
- ### Performance Issues
233
- - Check hook execution times in results
234
- - Consider caching expensive operations
235
- - Profile hooks with `cProfile` if needed
236
-
237
- ## Examples
238
-
239
- See the `builtin/` directory for example implementations:
240
- - `submit_hook_example.py`: Ticket and priority detection
241
- - `pre_delegation_hook_example.py`: Context filtering and enhancement
242
- - `post_delegation_hook_example.py`: Result validation and metrics
243
- - `ticket_extraction_hook_example.py`: Automatic ticket extraction
71
+ Then run Claude Code from that terminal. Hook events will be logged to `~/.claude-mpm/logs/`.
72
+
73
+ ## Legacy Hook System (Deprecated)
74
+
75
+ The `builtin/` directory contains the old internal hook system that was designed for JSON-RPC based hooks. This system is deprecated and will be removed in a future version. All hook functionality is now handled through the Claude Code hooks.
76
+
77
+ ## Development
78
+
79
+ To add new `/mpm` commands:
80
+
81
+ 1. Edit `hook_handler.py` to handle the new command
82
+ 2. Update the help text in the `handle_mpm_help()` function
83
+ 3. Test by running Claude Code with the new command
84
+
85
+ ## Exit Codes
86
+
87
+ The hook system uses specific exit codes:
88
+
89
+ - `0` - Success, continue normal processing
90
+ - `2` - Block LLM processing (command was handled)
91
+ - Other - Error occurred
92
+
93
+ ## Environment Variables
94
+
95
+ - `CLAUDE_MPM_LOG_LEVEL` - Set to DEBUG for detailed logging
96
+ - `HOOK_EVENT_TYPE` - Set by Claude Code (UserPromptSubmit, PreToolUse, PostToolUse)
97
+ - `HOOK_DATA` - JSON data from Claude Code with event details
@@ -0,0 +1,125 @@
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
+ )
@@ -141,10 +141,11 @@ class TodoAgentPrefixHook(BaseHook):
141
141
 
142
142
  def _has_agent_prefix(self, content: str) -> bool:
143
143
  """Check if content already has an agent prefix."""
144
+ import re
144
145
  content = content.strip()
145
- # Check if content has a valid agent prefix
146
- agent = agent_name_normalizer.extract_from_todo(content)
147
- return agent is not None
146
+ # Only check for [Agent] prefix at the beginning, not agent mentions in content
147
+ match = re.match(r'^\[([^\]]+)\]', content)
148
+ return match is not None
148
149
 
149
150
  def _suggest_agent(self, content: str) -> Optional[str]:
150
151
  """Suggest an appropriate agent based on content analysis."""
@@ -158,13 +159,13 @@ class TodoAgentPrefixHook(BaseHook):
158
159
 
159
160
  # Default suggestions based on common keywords
160
161
  if any(word in content_lower for word in ['code', 'implement', 'fix', 'bug']):
161
- return 'engineer'
162
+ return agent_name_normalizer.normalize('engineer')
162
163
  elif any(word in content_lower for word in ['test', 'validate', 'check']):
163
- return 'qa'
164
+ return agent_name_normalizer.normalize('qa')
164
165
  elif any(word in content_lower for word in ['doc', 'readme', 'guide']):
165
- return 'documentation'
166
+ return agent_name_normalizer.normalize('documentation')
166
167
  elif any(word in content_lower for word in ['research', 'investigate']):
167
- return 'research'
168
+ return agent_name_normalizer.normalize('research')
168
169
 
169
170
  return None
170
171
 
@@ -0,0 +1,5 @@
1
+ """Claude Code hooks integration for claude-mpm."""
2
+
3
+ from .hook_handler import ClaudeHookHandler
4
+
5
+ __all__ = ['ClaudeHookHandler']