claude-mpm 0.3.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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (159) hide show
  1. claude_mpm/__init__.py +17 -0
  2. claude_mpm/__main__.py +14 -0
  3. claude_mpm/_version.py +32 -0
  4. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +88 -0
  5. claude_mpm/agents/INSTRUCTIONS.md +375 -0
  6. claude_mpm/agents/__init__.py +118 -0
  7. claude_mpm/agents/agent_loader.py +621 -0
  8. claude_mpm/agents/agent_loader_integration.py +229 -0
  9. claude_mpm/agents/agents_metadata.py +204 -0
  10. claude_mpm/agents/base_agent.json +27 -0
  11. claude_mpm/agents/base_agent_loader.py +519 -0
  12. claude_mpm/agents/schema/agent_schema.json +160 -0
  13. claude_mpm/agents/system_agent_config.py +587 -0
  14. claude_mpm/agents/templates/__init__.py +101 -0
  15. claude_mpm/agents/templates/data_engineer_agent.json +46 -0
  16. claude_mpm/agents/templates/documentation_agent.json +45 -0
  17. claude_mpm/agents/templates/engineer_agent.json +49 -0
  18. claude_mpm/agents/templates/ops_agent.json +46 -0
  19. claude_mpm/agents/templates/qa_agent.json +45 -0
  20. claude_mpm/agents/templates/research_agent.json +49 -0
  21. claude_mpm/agents/templates/security_agent.json +46 -0
  22. claude_mpm/agents/templates/update-optimized-specialized-agents.json +374 -0
  23. claude_mpm/agents/templates/version_control_agent.json +46 -0
  24. claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +6 -0
  25. claude_mpm/cli.py +655 -0
  26. claude_mpm/cli_main.py +13 -0
  27. claude_mpm/cli_module/__init__.py +15 -0
  28. claude_mpm/cli_module/args.py +222 -0
  29. claude_mpm/cli_module/commands.py +203 -0
  30. claude_mpm/cli_module/migration_example.py +183 -0
  31. claude_mpm/cli_module/refactoring_guide.md +253 -0
  32. claude_mpm/cli_old/__init__.py +1 -0
  33. claude_mpm/cli_old/ticket_cli.py +102 -0
  34. claude_mpm/config/__init__.py +5 -0
  35. claude_mpm/config/hook_config.py +42 -0
  36. claude_mpm/constants.py +150 -0
  37. claude_mpm/core/__init__.py +45 -0
  38. claude_mpm/core/agent_name_normalizer.py +248 -0
  39. claude_mpm/core/agent_registry.py +627 -0
  40. claude_mpm/core/agent_registry.py.bak +312 -0
  41. claude_mpm/core/agent_session_manager.py +273 -0
  42. claude_mpm/core/base_service.py +747 -0
  43. claude_mpm/core/base_service.py.bak +406 -0
  44. claude_mpm/core/config.py +334 -0
  45. claude_mpm/core/config_aliases.py +292 -0
  46. claude_mpm/core/container.py +347 -0
  47. claude_mpm/core/factories.py +281 -0
  48. claude_mpm/core/framework_loader.py +472 -0
  49. claude_mpm/core/injectable_service.py +206 -0
  50. claude_mpm/core/interfaces.py +539 -0
  51. claude_mpm/core/logger.py +468 -0
  52. claude_mpm/core/minimal_framework_loader.py +107 -0
  53. claude_mpm/core/mixins.py +150 -0
  54. claude_mpm/core/service_registry.py +299 -0
  55. claude_mpm/core/session_manager.py +190 -0
  56. claude_mpm/core/simple_runner.py +511 -0
  57. claude_mpm/core/tool_access_control.py +173 -0
  58. claude_mpm/hooks/README.md +243 -0
  59. claude_mpm/hooks/__init__.py +5 -0
  60. claude_mpm/hooks/base_hook.py +154 -0
  61. claude_mpm/hooks/builtin/__init__.py +1 -0
  62. claude_mpm/hooks/builtin/logging_hook_example.py +165 -0
  63. claude_mpm/hooks/builtin/post_delegation_hook_example.py +124 -0
  64. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +125 -0
  65. claude_mpm/hooks/builtin/submit_hook_example.py +100 -0
  66. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +237 -0
  67. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +239 -0
  68. claude_mpm/hooks/builtin/workflow_start_hook.py +181 -0
  69. claude_mpm/hooks/hook_client.py +264 -0
  70. claude_mpm/hooks/hook_runner.py +370 -0
  71. claude_mpm/hooks/json_rpc_executor.py +259 -0
  72. claude_mpm/hooks/json_rpc_hook_client.py +319 -0
  73. claude_mpm/hooks/tool_call_interceptor.py +204 -0
  74. claude_mpm/init.py +246 -0
  75. claude_mpm/orchestration/SUBPROCESS_DESIGN.md +66 -0
  76. claude_mpm/orchestration/__init__.py +6 -0
  77. claude_mpm/orchestration/archive/direct_orchestrator.py +195 -0
  78. claude_mpm/orchestration/archive/factory.py +215 -0
  79. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +188 -0
  80. claude_mpm/orchestration/archive/hook_integration_example.py +178 -0
  81. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +826 -0
  82. claude_mpm/orchestration/archive/orchestrator.py +501 -0
  83. claude_mpm/orchestration/archive/pexpect_orchestrator.py +252 -0
  84. claude_mpm/orchestration/archive/pty_orchestrator.py +270 -0
  85. claude_mpm/orchestration/archive/simple_orchestrator.py +82 -0
  86. claude_mpm/orchestration/archive/subprocess_orchestrator.py +801 -0
  87. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +278 -0
  88. claude_mpm/orchestration/archive/wrapper_orchestrator.py +187 -0
  89. claude_mpm/scripts/__init__.py +1 -0
  90. claude_mpm/scripts/ticket.py +269 -0
  91. claude_mpm/services/__init__.py +10 -0
  92. claude_mpm/services/agent_deployment.py +955 -0
  93. claude_mpm/services/agent_lifecycle_manager.py +948 -0
  94. claude_mpm/services/agent_management_service.py +596 -0
  95. claude_mpm/services/agent_modification_tracker.py +841 -0
  96. claude_mpm/services/agent_profile_loader.py +606 -0
  97. claude_mpm/services/agent_registry.py +677 -0
  98. claude_mpm/services/base_agent_manager.py +380 -0
  99. claude_mpm/services/framework_agent_loader.py +337 -0
  100. claude_mpm/services/framework_claude_md_generator/README.md +92 -0
  101. claude_mpm/services/framework_claude_md_generator/__init__.py +206 -0
  102. claude_mpm/services/framework_claude_md_generator/content_assembler.py +151 -0
  103. claude_mpm/services/framework_claude_md_generator/content_validator.py +126 -0
  104. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +137 -0
  105. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +106 -0
  106. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +582 -0
  107. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +97 -0
  108. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +27 -0
  109. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +23 -0
  110. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +23 -0
  111. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +20 -0
  112. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +26 -0
  113. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +30 -0
  114. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +37 -0
  115. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +111 -0
  116. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +89 -0
  117. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +39 -0
  118. claude_mpm/services/framework_claude_md_generator/section_manager.py +106 -0
  119. claude_mpm/services/framework_claude_md_generator/version_manager.py +121 -0
  120. claude_mpm/services/framework_claude_md_generator.py +621 -0
  121. claude_mpm/services/hook_service.py +388 -0
  122. claude_mpm/services/hook_service_manager.py +223 -0
  123. claude_mpm/services/json_rpc_hook_manager.py +92 -0
  124. claude_mpm/services/parent_directory_manager/README.md +83 -0
  125. claude_mpm/services/parent_directory_manager/__init__.py +577 -0
  126. claude_mpm/services/parent_directory_manager/backup_manager.py +258 -0
  127. claude_mpm/services/parent_directory_manager/config_manager.py +210 -0
  128. claude_mpm/services/parent_directory_manager/deduplication_manager.py +279 -0
  129. claude_mpm/services/parent_directory_manager/framework_protector.py +143 -0
  130. claude_mpm/services/parent_directory_manager/operations.py +186 -0
  131. claude_mpm/services/parent_directory_manager/state_manager.py +624 -0
  132. claude_mpm/services/parent_directory_manager/template_deployer.py +579 -0
  133. claude_mpm/services/parent_directory_manager/validation_manager.py +378 -0
  134. claude_mpm/services/parent_directory_manager/version_control_helper.py +339 -0
  135. claude_mpm/services/parent_directory_manager/version_manager.py +222 -0
  136. claude_mpm/services/shared_prompt_cache.py +819 -0
  137. claude_mpm/services/ticket_manager.py +213 -0
  138. claude_mpm/services/ticket_manager_di.py +318 -0
  139. claude_mpm/services/ticketing_service_original.py +508 -0
  140. claude_mpm/services/version_control/VERSION +1 -0
  141. claude_mpm/services/version_control/__init__.py +70 -0
  142. claude_mpm/services/version_control/branch_strategy.py +670 -0
  143. claude_mpm/services/version_control/conflict_resolution.py +744 -0
  144. claude_mpm/services/version_control/git_operations.py +784 -0
  145. claude_mpm/services/version_control/semantic_versioning.py +703 -0
  146. claude_mpm/ui/__init__.py +1 -0
  147. claude_mpm/ui/rich_terminal_ui.py +295 -0
  148. claude_mpm/ui/terminal_ui.py +328 -0
  149. claude_mpm/utils/__init__.py +16 -0
  150. claude_mpm/utils/config_manager.py +468 -0
  151. claude_mpm/utils/import_migration_example.py +80 -0
  152. claude_mpm/utils/imports.py +182 -0
  153. claude_mpm/utils/path_operations.py +357 -0
  154. claude_mpm/utils/paths.py +289 -0
  155. claude_mpm-0.3.0.dist-info/METADATA +290 -0
  156. claude_mpm-0.3.0.dist-info/RECORD +159 -0
  157. claude_mpm-0.3.0.dist-info/WHEEL +5 -0
  158. claude_mpm-0.3.0.dist-info/entry_points.txt +4 -0
  159. claude_mpm-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,243 @@
1
+ # Claude MPM Hook System
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.
8
+
9
+ ## Overview
10
+
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
17
+
18
+ ## Architecture
19
+
20
+ ```
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
+ └─────────────────┘
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Basic Client Usage
34
+ ```python
35
+ from claude_mpm.hooks.json_rpc_hook_client import JSONRPCHookClient
36
+
37
+ # Create client
38
+ client = JSONRPCHookClient()
39
+
40
+ # Check system health
41
+ health = client.health_check()
42
+ print(f"Status: {health['status']}")
43
+ print(f"Hooks available: {health['hook_count']}")
44
+
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
+ ```
50
+
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)
75
+ ```
76
+
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
+ ```
98
+
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
+ ```
117
+
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
+ ```
137
+
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
+ ```
157
+
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()
187
+ ```
188
+
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
@@ -0,0 +1,5 @@
1
+ """Hook system for claude-mpm."""
2
+
3
+ from .base_hook import BaseHook, HookContext, HookResult, HookType
4
+
5
+ __all__ = ["BaseHook", "HookContext", "HookResult", "HookType"]
@@ -0,0 +1,154 @@
1
+ """Base hook class and types for claude-mpm hook system."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from dataclasses import dataclass
5
+ from enum import Enum
6
+ from typing import Any, Dict, List, Optional, Union
7
+ import asyncio
8
+ import logging
9
+ from datetime import datetime
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class HookType(Enum):
15
+ """Types of hooks available in the system."""
16
+ SUBMIT = "submit" # Process user prompts
17
+ PRE_DELEGATION = "pre_delegation" # Filter context before delegation
18
+ POST_DELEGATION = "post_delegation" # Process results after delegation
19
+ TICKET_EXTRACTION = "ticket_extraction" # Extract and create tickets
20
+ CUSTOM = "custom" # User-defined hooks
21
+
22
+
23
+ @dataclass
24
+ class HookContext:
25
+ """Context passed to hooks for processing."""
26
+ hook_type: HookType
27
+ data: Dict[str, Any]
28
+ metadata: Dict[str, Any]
29
+ timestamp: datetime
30
+ session_id: Optional[str] = None
31
+ user_id: Optional[str] = None
32
+
33
+ def __post_init__(self):
34
+ """Ensure timestamp is set."""
35
+ if not hasattr(self, 'timestamp') or self.timestamp is None:
36
+ self.timestamp = datetime.now()
37
+
38
+
39
+ @dataclass
40
+ class HookResult:
41
+ """Result returned from hook execution."""
42
+ success: bool
43
+ data: Optional[Dict[str, Any]] = None
44
+ error: Optional[str] = None
45
+ modified: bool = False
46
+ metadata: Optional[Dict[str, Any]] = None
47
+ execution_time_ms: Optional[float] = None
48
+
49
+
50
+ class BaseHook(ABC):
51
+ """Base class for all hooks."""
52
+
53
+ def __init__(self, name: str, priority: int = 50):
54
+ """Initialize hook with name and priority.
55
+
56
+ Args:
57
+ name: Unique name for the hook
58
+ priority: Execution priority (0-100, lower executes first)
59
+ """
60
+ self.name = name
61
+ self.priority = max(0, min(100, priority)) # Clamp to 0-100
62
+ self.enabled = True
63
+ self._async = False
64
+
65
+ @abstractmethod
66
+ def execute(self, context: HookContext) -> HookResult:
67
+ """Execute the hook with given context.
68
+
69
+ Args:
70
+ context: Hook context containing data and metadata
71
+
72
+ Returns:
73
+ HookResult with execution results
74
+ """
75
+ pass
76
+
77
+ async def async_execute(self, context: HookContext) -> HookResult:
78
+ """Async version of execute. Override for async hooks."""
79
+ # Default implementation calls sync execute in executor
80
+ loop = asyncio.get_event_loop()
81
+ return await loop.run_in_executor(None, self.execute, context)
82
+
83
+ def validate(self, context: HookContext) -> bool:
84
+ """Validate if hook should run for given context.
85
+
86
+ Args:
87
+ context: Hook context to validate
88
+
89
+ Returns:
90
+ True if hook should execute, False otherwise
91
+ """
92
+ return self.enabled
93
+
94
+ def __repr__(self):
95
+ """String representation of hook."""
96
+ return f"{self.__class__.__name__}(name='{self.name}', priority={self.priority}, enabled={self.enabled})"
97
+
98
+ def __lt__(self, other):
99
+ """Compare hooks by priority for sorting."""
100
+ if not isinstance(other, BaseHook):
101
+ return NotImplemented
102
+ return self.priority < other.priority
103
+
104
+
105
+ class SubmitHook(BaseHook):
106
+ """Base class for hooks that process user prompts."""
107
+
108
+ def __init__(self, name: str, priority: int = 50):
109
+ super().__init__(name, priority)
110
+
111
+ def validate(self, context: HookContext) -> bool:
112
+ """Validate submit hook context."""
113
+ if not super().validate(context):
114
+ return False
115
+ return context.hook_type == HookType.SUBMIT and 'prompt' in context.data
116
+
117
+
118
+ class PreDelegationHook(BaseHook):
119
+ """Base class for hooks that filter context before delegation."""
120
+
121
+ def __init__(self, name: str, priority: int = 50):
122
+ super().__init__(name, priority)
123
+
124
+ def validate(self, context: HookContext) -> bool:
125
+ """Validate pre-delegation hook context."""
126
+ if not super().validate(context):
127
+ return False
128
+ return context.hook_type == HookType.PRE_DELEGATION and 'agent' in context.data
129
+
130
+
131
+ class PostDelegationHook(BaseHook):
132
+ """Base class for hooks that process results after delegation."""
133
+
134
+ def __init__(self, name: str, priority: int = 50):
135
+ super().__init__(name, priority)
136
+
137
+ def validate(self, context: HookContext) -> bool:
138
+ """Validate post-delegation hook context."""
139
+ if not super().validate(context):
140
+ return False
141
+ return context.hook_type == HookType.POST_DELEGATION and 'result' in context.data
142
+
143
+
144
+ class TicketExtractionHook(BaseHook):
145
+ """Base class for hooks that extract and create tickets."""
146
+
147
+ def __init__(self, name: str, priority: int = 50):
148
+ super().__init__(name, priority)
149
+
150
+ def validate(self, context: HookContext) -> bool:
151
+ """Validate ticket extraction hook context."""
152
+ if not super().validate(context):
153
+ return False
154
+ return context.hook_type == HookType.TICKET_EXTRACTION
@@ -0,0 +1 @@
1
+ """Built-in hooks for claude-mpm."""
@@ -0,0 +1,165 @@
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}")
@@ -0,0 +1,124 @@
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
+ )