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.
- claude_mpm/__init__.py +17 -0
- claude_mpm/__main__.py +14 -0
- claude_mpm/_version.py +32 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +88 -0
- claude_mpm/agents/INSTRUCTIONS.md +375 -0
- claude_mpm/agents/__init__.py +118 -0
- claude_mpm/agents/agent_loader.py +621 -0
- claude_mpm/agents/agent_loader_integration.py +229 -0
- claude_mpm/agents/agents_metadata.py +204 -0
- claude_mpm/agents/base_agent.json +27 -0
- claude_mpm/agents/base_agent_loader.py +519 -0
- claude_mpm/agents/schema/agent_schema.json +160 -0
- claude_mpm/agents/system_agent_config.py +587 -0
- claude_mpm/agents/templates/__init__.py +101 -0
- claude_mpm/agents/templates/data_engineer_agent.json +46 -0
- claude_mpm/agents/templates/documentation_agent.json +45 -0
- claude_mpm/agents/templates/engineer_agent.json +49 -0
- claude_mpm/agents/templates/ops_agent.json +46 -0
- claude_mpm/agents/templates/qa_agent.json +45 -0
- claude_mpm/agents/templates/research_agent.json +49 -0
- claude_mpm/agents/templates/security_agent.json +46 -0
- claude_mpm/agents/templates/update-optimized-specialized-agents.json +374 -0
- claude_mpm/agents/templates/version_control_agent.json +46 -0
- claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +6 -0
- claude_mpm/cli.py +655 -0
- claude_mpm/cli_main.py +13 -0
- claude_mpm/cli_module/__init__.py +15 -0
- claude_mpm/cli_module/args.py +222 -0
- claude_mpm/cli_module/commands.py +203 -0
- claude_mpm/cli_module/migration_example.py +183 -0
- claude_mpm/cli_module/refactoring_guide.md +253 -0
- claude_mpm/cli_old/__init__.py +1 -0
- claude_mpm/cli_old/ticket_cli.py +102 -0
- claude_mpm/config/__init__.py +5 -0
- claude_mpm/config/hook_config.py +42 -0
- claude_mpm/constants.py +150 -0
- claude_mpm/core/__init__.py +45 -0
- claude_mpm/core/agent_name_normalizer.py +248 -0
- claude_mpm/core/agent_registry.py +627 -0
- claude_mpm/core/agent_registry.py.bak +312 -0
- claude_mpm/core/agent_session_manager.py +273 -0
- claude_mpm/core/base_service.py +747 -0
- claude_mpm/core/base_service.py.bak +406 -0
- claude_mpm/core/config.py +334 -0
- claude_mpm/core/config_aliases.py +292 -0
- claude_mpm/core/container.py +347 -0
- claude_mpm/core/factories.py +281 -0
- claude_mpm/core/framework_loader.py +472 -0
- claude_mpm/core/injectable_service.py +206 -0
- claude_mpm/core/interfaces.py +539 -0
- claude_mpm/core/logger.py +468 -0
- claude_mpm/core/minimal_framework_loader.py +107 -0
- claude_mpm/core/mixins.py +150 -0
- claude_mpm/core/service_registry.py +299 -0
- claude_mpm/core/session_manager.py +190 -0
- claude_mpm/core/simple_runner.py +511 -0
- claude_mpm/core/tool_access_control.py +173 -0
- claude_mpm/hooks/README.md +243 -0
- claude_mpm/hooks/__init__.py +5 -0
- claude_mpm/hooks/base_hook.py +154 -0
- claude_mpm/hooks/builtin/__init__.py +1 -0
- claude_mpm/hooks/builtin/logging_hook_example.py +165 -0
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +124 -0
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +125 -0
- claude_mpm/hooks/builtin/submit_hook_example.py +100 -0
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +237 -0
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +239 -0
- claude_mpm/hooks/builtin/workflow_start_hook.py +181 -0
- claude_mpm/hooks/hook_client.py +264 -0
- claude_mpm/hooks/hook_runner.py +370 -0
- claude_mpm/hooks/json_rpc_executor.py +259 -0
- claude_mpm/hooks/json_rpc_hook_client.py +319 -0
- claude_mpm/hooks/tool_call_interceptor.py +204 -0
- claude_mpm/init.py +246 -0
- claude_mpm/orchestration/SUBPROCESS_DESIGN.md +66 -0
- claude_mpm/orchestration/__init__.py +6 -0
- claude_mpm/orchestration/archive/direct_orchestrator.py +195 -0
- claude_mpm/orchestration/archive/factory.py +215 -0
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +188 -0
- claude_mpm/orchestration/archive/hook_integration_example.py +178 -0
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +826 -0
- claude_mpm/orchestration/archive/orchestrator.py +501 -0
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +252 -0
- claude_mpm/orchestration/archive/pty_orchestrator.py +270 -0
- claude_mpm/orchestration/archive/simple_orchestrator.py +82 -0
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +801 -0
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +278 -0
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +187 -0
- claude_mpm/scripts/__init__.py +1 -0
- claude_mpm/scripts/ticket.py +269 -0
- claude_mpm/services/__init__.py +10 -0
- claude_mpm/services/agent_deployment.py +955 -0
- claude_mpm/services/agent_lifecycle_manager.py +948 -0
- claude_mpm/services/agent_management_service.py +596 -0
- claude_mpm/services/agent_modification_tracker.py +841 -0
- claude_mpm/services/agent_profile_loader.py +606 -0
- claude_mpm/services/agent_registry.py +677 -0
- claude_mpm/services/base_agent_manager.py +380 -0
- claude_mpm/services/framework_agent_loader.py +337 -0
- claude_mpm/services/framework_claude_md_generator/README.md +92 -0
- claude_mpm/services/framework_claude_md_generator/__init__.py +206 -0
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +151 -0
- claude_mpm/services/framework_claude_md_generator/content_validator.py +126 -0
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +137 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +106 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +582 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +97 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +27 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +23 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +23 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +20 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +26 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +30 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +37 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +111 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +89 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +39 -0
- claude_mpm/services/framework_claude_md_generator/section_manager.py +106 -0
- claude_mpm/services/framework_claude_md_generator/version_manager.py +121 -0
- claude_mpm/services/framework_claude_md_generator.py +621 -0
- claude_mpm/services/hook_service.py +388 -0
- claude_mpm/services/hook_service_manager.py +223 -0
- claude_mpm/services/json_rpc_hook_manager.py +92 -0
- claude_mpm/services/parent_directory_manager/README.md +83 -0
- claude_mpm/services/parent_directory_manager/__init__.py +577 -0
- claude_mpm/services/parent_directory_manager/backup_manager.py +258 -0
- claude_mpm/services/parent_directory_manager/config_manager.py +210 -0
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +279 -0
- claude_mpm/services/parent_directory_manager/framework_protector.py +143 -0
- claude_mpm/services/parent_directory_manager/operations.py +186 -0
- claude_mpm/services/parent_directory_manager/state_manager.py +624 -0
- claude_mpm/services/parent_directory_manager/template_deployer.py +579 -0
- claude_mpm/services/parent_directory_manager/validation_manager.py +378 -0
- claude_mpm/services/parent_directory_manager/version_control_helper.py +339 -0
- claude_mpm/services/parent_directory_manager/version_manager.py +222 -0
- claude_mpm/services/shared_prompt_cache.py +819 -0
- claude_mpm/services/ticket_manager.py +213 -0
- claude_mpm/services/ticket_manager_di.py +318 -0
- claude_mpm/services/ticketing_service_original.py +508 -0
- claude_mpm/services/version_control/VERSION +1 -0
- claude_mpm/services/version_control/__init__.py +70 -0
- claude_mpm/services/version_control/branch_strategy.py +670 -0
- claude_mpm/services/version_control/conflict_resolution.py +744 -0
- claude_mpm/services/version_control/git_operations.py +784 -0
- claude_mpm/services/version_control/semantic_versioning.py +703 -0
- claude_mpm/ui/__init__.py +1 -0
- claude_mpm/ui/rich_terminal_ui.py +295 -0
- claude_mpm/ui/terminal_ui.py +328 -0
- claude_mpm/utils/__init__.py +16 -0
- claude_mpm/utils/config_manager.py +468 -0
- claude_mpm/utils/import_migration_example.py +80 -0
- claude_mpm/utils/imports.py +182 -0
- claude_mpm/utils/path_operations.py +357 -0
- claude_mpm/utils/paths.py +289 -0
- claude_mpm-0.3.0.dist-info/METADATA +290 -0
- claude_mpm-0.3.0.dist-info/RECORD +159 -0
- claude_mpm-0.3.0.dist-info/WHEEL +5 -0
- claude_mpm-0.3.0.dist-info/entry_points.txt +4 -0
- claude_mpm-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Orchestrator factory for creating orchestrators based on mode and configuration."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional, Dict, Type, Any
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
from ..core.logger import get_logger
|
|
9
|
+
from .orchestrator import MPMOrchestrator
|
|
10
|
+
from .system_prompt_orchestrator import SystemPromptOrchestrator
|
|
11
|
+
from .subprocess_orchestrator import SubprocessOrchestrator
|
|
12
|
+
from .interactive_subprocess_orchestrator import InteractiveSubprocessOrchestrator
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class OrchestratorMode(Enum):
|
|
16
|
+
"""Available orchestrator modes."""
|
|
17
|
+
SYSTEM_PROMPT = "system_prompt"
|
|
18
|
+
SUBPROCESS = "subprocess"
|
|
19
|
+
INTERACTIVE_SUBPROCESS = "interactive_subprocess"
|
|
20
|
+
DIRECT = "direct"
|
|
21
|
+
PTY = "pty"
|
|
22
|
+
WRAPPER = "wrapper"
|
|
23
|
+
SIMPLE = "simple"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OrchestratorFactory:
|
|
27
|
+
"""Factory for creating orchestrators based on mode and configuration.
|
|
28
|
+
|
|
29
|
+
This factory simplifies orchestrator selection and reduces complexity
|
|
30
|
+
in the run_session function by centralizing the logic for choosing
|
|
31
|
+
and instantiating the appropriate orchestrator.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
# Registry of available orchestrators
|
|
35
|
+
_registry: Dict[OrchestratorMode, Type[MPMOrchestrator]] = {
|
|
36
|
+
OrchestratorMode.SYSTEM_PROMPT: SystemPromptOrchestrator,
|
|
37
|
+
OrchestratorMode.SUBPROCESS: SubprocessOrchestrator,
|
|
38
|
+
OrchestratorMode.INTERACTIVE_SUBPROCESS: InteractiveSubprocessOrchestrator,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
def __init__(self):
|
|
42
|
+
"""Initialize the orchestrator factory."""
|
|
43
|
+
self.logger = get_logger(self.__class__.__name__)
|
|
44
|
+
self._discover_orchestrators()
|
|
45
|
+
|
|
46
|
+
def _discover_orchestrators(self):
|
|
47
|
+
"""Discover and register additional orchestrators.
|
|
48
|
+
|
|
49
|
+
This method enables automatic discovery of new orchestrator types
|
|
50
|
+
without modifying the factory code.
|
|
51
|
+
"""
|
|
52
|
+
# Try to import optional orchestrators
|
|
53
|
+
try:
|
|
54
|
+
from .direct_orchestrator import DirectOrchestrator
|
|
55
|
+
self._registry[OrchestratorMode.DIRECT] = DirectOrchestrator
|
|
56
|
+
self.logger.debug("Registered DirectOrchestrator")
|
|
57
|
+
except ImportError:
|
|
58
|
+
self.logger.debug("DirectOrchestrator not available")
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
from .pty_orchestrator import PTYOrchestrator
|
|
62
|
+
self._registry[OrchestratorMode.PTY] = PTYOrchestrator
|
|
63
|
+
self.logger.debug("Registered PTYOrchestrator")
|
|
64
|
+
except ImportError:
|
|
65
|
+
self.logger.debug("PTYOrchestrator not available")
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
from .wrapper_orchestrator import WrapperOrchestrator
|
|
69
|
+
self._registry[OrchestratorMode.WRAPPER] = WrapperOrchestrator
|
|
70
|
+
self.logger.debug("Registered WrapperOrchestrator")
|
|
71
|
+
except ImportError:
|
|
72
|
+
self.logger.debug("WrapperOrchestrator not available")
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
from .simple_orchestrator import SimpleOrchestrator
|
|
76
|
+
self._registry[OrchestratorMode.SIMPLE] = SimpleOrchestrator
|
|
77
|
+
self.logger.debug("Registered SimpleOrchestrator")
|
|
78
|
+
except ImportError:
|
|
79
|
+
self.logger.debug("SimpleOrchestrator not available")
|
|
80
|
+
|
|
81
|
+
def create_orchestrator(
|
|
82
|
+
self,
|
|
83
|
+
mode: Optional[str] = None,
|
|
84
|
+
config: Optional[Dict[str, Any]] = None
|
|
85
|
+
) -> MPMOrchestrator:
|
|
86
|
+
"""Create an orchestrator based on mode and configuration.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
mode: Orchestrator mode (string or OrchestratorMode enum)
|
|
90
|
+
config: Configuration dictionary containing:
|
|
91
|
+
- framework_path: Path to framework directory
|
|
92
|
+
- agents_dir: Custom agents directory
|
|
93
|
+
- log_level: Logging level (OFF, INFO, DEBUG)
|
|
94
|
+
- log_dir: Custom log directory
|
|
95
|
+
- hook_manager: Hook service manager instance
|
|
96
|
+
- enable_todo_hijacking: Enable TODO hijacking (subprocess mode)
|
|
97
|
+
- subprocess: Use subprocess orchestration
|
|
98
|
+
- interactive_subprocess: Use interactive subprocess
|
|
99
|
+
- Any other orchestrator-specific parameters
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Configured orchestrator instance
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
ValueError: If mode is invalid or orchestrator creation fails
|
|
106
|
+
"""
|
|
107
|
+
if config is None:
|
|
108
|
+
config = {}
|
|
109
|
+
|
|
110
|
+
# Determine orchestrator mode
|
|
111
|
+
orchestrator_mode = self._determine_mode(mode, config)
|
|
112
|
+
|
|
113
|
+
# Validate mode
|
|
114
|
+
if orchestrator_mode not in self._registry:
|
|
115
|
+
available = ", ".join(m.value for m in self._registry.keys())
|
|
116
|
+
raise ValueError(
|
|
117
|
+
f"Invalid orchestrator mode: {orchestrator_mode.value}. "
|
|
118
|
+
f"Available modes: {available}"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Get orchestrator class
|
|
122
|
+
orchestrator_class = self._registry[orchestrator_mode]
|
|
123
|
+
|
|
124
|
+
# Extract common parameters
|
|
125
|
+
common_params = {
|
|
126
|
+
"framework_path": config.get("framework_path"),
|
|
127
|
+
"agents_dir": config.get("agents_dir"),
|
|
128
|
+
"log_level": config.get("log_level", "OFF"),
|
|
129
|
+
"log_dir": config.get("log_dir"),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# Add hook manager if available
|
|
133
|
+
if "hook_manager" in config:
|
|
134
|
+
common_params["hook_manager"] = config["hook_manager"]
|
|
135
|
+
|
|
136
|
+
# Add mode-specific parameters
|
|
137
|
+
if orchestrator_mode == OrchestratorMode.SUBPROCESS:
|
|
138
|
+
common_params["enable_todo_hijacking"] = config.get("enable_todo_hijacking", False)
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
# Create orchestrator instance
|
|
142
|
+
orchestrator = orchestrator_class(**common_params)
|
|
143
|
+
|
|
144
|
+
# Configure additional settings
|
|
145
|
+
if "no_tickets" in config and config["no_tickets"]:
|
|
146
|
+
orchestrator.ticket_creation_enabled = False
|
|
147
|
+
self.logger.info("Ticket creation disabled for orchestrator")
|
|
148
|
+
|
|
149
|
+
self.logger.info(
|
|
150
|
+
f"Created {orchestrator_class.__name__} "
|
|
151
|
+
f"(mode: {orchestrator_mode.value})"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
return orchestrator
|
|
155
|
+
|
|
156
|
+
except Exception as e:
|
|
157
|
+
self.logger.error(f"Failed to create orchestrator: {e}")
|
|
158
|
+
raise ValueError(f"Failed to create orchestrator: {e}") from e
|
|
159
|
+
|
|
160
|
+
def _determine_mode(
|
|
161
|
+
self,
|
|
162
|
+
mode: Optional[str],
|
|
163
|
+
config: Dict[str, Any]
|
|
164
|
+
) -> OrchestratorMode:
|
|
165
|
+
"""Determine orchestrator mode from explicit mode or config flags.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
mode: Explicit mode string
|
|
169
|
+
config: Configuration dictionary
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
OrchestratorMode enum value
|
|
173
|
+
"""
|
|
174
|
+
# Always use subprocess orchestrator for simplicity
|
|
175
|
+
# This provides consistent behavior in both interactive and non-interactive modes
|
|
176
|
+
return OrchestratorMode.SUBPROCESS
|
|
177
|
+
|
|
178
|
+
def list_available_modes(self) -> Dict[str, Dict[str, Any]]:
|
|
179
|
+
"""List all available orchestrator modes with metadata.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Dictionary mapping mode names to metadata
|
|
183
|
+
"""
|
|
184
|
+
modes = {}
|
|
185
|
+
for mode, orchestrator_class in self._registry.items():
|
|
186
|
+
modes[mode.value] = {
|
|
187
|
+
"class": orchestrator_class.__name__,
|
|
188
|
+
"module": orchestrator_class.__module__,
|
|
189
|
+
"description": orchestrator_class.__doc__.strip() if orchestrator_class.__doc__ else "No description",
|
|
190
|
+
}
|
|
191
|
+
return modes
|
|
192
|
+
|
|
193
|
+
def register_orchestrator(
|
|
194
|
+
self,
|
|
195
|
+
mode: OrchestratorMode,
|
|
196
|
+
orchestrator_class: Type[MPMOrchestrator]
|
|
197
|
+
):
|
|
198
|
+
"""Register a custom orchestrator.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
mode: Orchestrator mode
|
|
202
|
+
orchestrator_class: Orchestrator class (must inherit from MPMOrchestrator)
|
|
203
|
+
|
|
204
|
+
Raises:
|
|
205
|
+
ValueError: If orchestrator class is invalid
|
|
206
|
+
"""
|
|
207
|
+
if not issubclass(orchestrator_class, MPMOrchestrator):
|
|
208
|
+
raise ValueError(
|
|
209
|
+
f"{orchestrator_class.__name__} must inherit from MPMOrchestrator"
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
self._registry[mode] = orchestrator_class
|
|
213
|
+
self.logger.info(
|
|
214
|
+
f"Registered {orchestrator_class.__name__} for mode {mode.value}"
|
|
215
|
+
)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Example orchestrator that integrates with the hook service."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from src.hooks.hook_client import get_hook_client, HookServiceClient
|
|
8
|
+
from src.hooks.base_hook import HookType
|
|
9
|
+
from src.orchestration.orchestrator import Orchestrator
|
|
10
|
+
from src.services.ticket_manager import TicketManager
|
|
11
|
+
from src.utils.logger import get_logger
|
|
12
|
+
|
|
13
|
+
logger = get_logger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class HookEnabledOrchestrator(Orchestrator):
|
|
17
|
+
"""Orchestrator that integrates with the centralized hook service."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, *args, **kwargs):
|
|
20
|
+
"""Initialize hook-enabled orchestrator."""
|
|
21
|
+
super().__init__(*args, **kwargs)
|
|
22
|
+
|
|
23
|
+
# Initialize hook client
|
|
24
|
+
self.hook_client = get_hook_client()
|
|
25
|
+
|
|
26
|
+
# Check hook service health
|
|
27
|
+
health = self.hook_client.health_check()
|
|
28
|
+
if health.get('status') == 'healthy':
|
|
29
|
+
logger.info(f"Hook service is healthy with {health.get('hooks_count', 0)} hooks")
|
|
30
|
+
else:
|
|
31
|
+
logger.warning(f"Hook service is not available: {health.get('error')}")
|
|
32
|
+
|
|
33
|
+
async def process_prompt(self, prompt: str) -> str:
|
|
34
|
+
"""Process user prompt with hook integration.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
prompt: User prompt to process
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Response from orchestration
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
# Execute submit hooks
|
|
44
|
+
logger.debug("Executing submit hooks...")
|
|
45
|
+
submit_results = self.hook_client.execute_submit_hook(
|
|
46
|
+
prompt=prompt,
|
|
47
|
+
session_id=getattr(self, 'session_id', None)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Get modified prompt data
|
|
51
|
+
modified_data = self.hook_client.get_modified_data(submit_results)
|
|
52
|
+
if modified_data.get('prompt'):
|
|
53
|
+
prompt = modified_data['prompt']
|
|
54
|
+
|
|
55
|
+
# Check for priority override
|
|
56
|
+
if modified_data.get('priority'):
|
|
57
|
+
logger.info(f"Priority detected: {modified_data['priority']}")
|
|
58
|
+
|
|
59
|
+
# Process prompt through normal orchestration
|
|
60
|
+
response = await super().process_prompt(prompt)
|
|
61
|
+
|
|
62
|
+
# Execute ticket extraction hooks on the conversation
|
|
63
|
+
logger.debug("Executing ticket extraction hooks...")
|
|
64
|
+
extraction_results = self.hook_client.execute_ticket_extraction_hook(
|
|
65
|
+
content={
|
|
66
|
+
'prompt': prompt,
|
|
67
|
+
'response': response
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Extract and create tickets
|
|
72
|
+
tickets = self.hook_client.get_extracted_tickets(extraction_results)
|
|
73
|
+
if tickets:
|
|
74
|
+
logger.info(f"Extracted {len(tickets)} tickets from conversation")
|
|
75
|
+
await self._create_tickets(tickets)
|
|
76
|
+
|
|
77
|
+
return response
|
|
78
|
+
|
|
79
|
+
except Exception as e:
|
|
80
|
+
logger.error(f"Error in hook-enabled orchestration: {e}")
|
|
81
|
+
# Fallback to normal orchestration if hooks fail
|
|
82
|
+
return await super().process_prompt(prompt)
|
|
83
|
+
|
|
84
|
+
async def delegate_to_agent(self, agent_name: str, context: Dict[str, Any]) -> Any:
|
|
85
|
+
"""Delegate to agent with hook integration.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
agent_name: Name of agent to delegate to
|
|
89
|
+
context: Context to pass to agent
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Result from agent
|
|
93
|
+
"""
|
|
94
|
+
try:
|
|
95
|
+
# Execute pre-delegation hooks
|
|
96
|
+
logger.debug(f"Executing pre-delegation hooks for {agent_name}...")
|
|
97
|
+
pre_results = self.hook_client.execute_pre_delegation_hook(
|
|
98
|
+
agent=agent_name,
|
|
99
|
+
context=context
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Get modified context
|
|
103
|
+
modified_data = self.hook_client.get_modified_data(pre_results)
|
|
104
|
+
if 'context' in modified_data:
|
|
105
|
+
context = modified_data['context']
|
|
106
|
+
logger.debug("Context modified by pre-delegation hooks")
|
|
107
|
+
|
|
108
|
+
# Delegate to agent
|
|
109
|
+
result = await super().delegate_to_agent(agent_name, context)
|
|
110
|
+
|
|
111
|
+
# Execute post-delegation hooks
|
|
112
|
+
logger.debug(f"Executing post-delegation hooks for {agent_name}...")
|
|
113
|
+
post_results = self.hook_client.execute_post_delegation_hook(
|
|
114
|
+
agent=agent_name,
|
|
115
|
+
result=result,
|
|
116
|
+
execution_time_ms=context.get('execution_time_ms')
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Get modified result
|
|
120
|
+
modified_data = self.hook_client.get_modified_data(post_results)
|
|
121
|
+
if 'result' in modified_data:
|
|
122
|
+
result = modified_data['result']
|
|
123
|
+
logger.debug("Result modified by post-delegation hooks")
|
|
124
|
+
|
|
125
|
+
# Check for validation issues
|
|
126
|
+
for post_result in post_results:
|
|
127
|
+
if 'validation_issues' in post_result.get('data', {}):
|
|
128
|
+
issues = post_result['data']['validation_issues']
|
|
129
|
+
logger.warning(f"Validation issues: {issues}")
|
|
130
|
+
|
|
131
|
+
return result
|
|
132
|
+
|
|
133
|
+
except Exception as e:
|
|
134
|
+
logger.error(f"Error in hook-enabled delegation: {e}")
|
|
135
|
+
# Fallback to normal delegation if hooks fail
|
|
136
|
+
return await super().delegate_to_agent(agent_name, context)
|
|
137
|
+
|
|
138
|
+
async def _create_tickets(self, tickets: List[Dict[str, Any]]):
|
|
139
|
+
"""Create tickets in the ticket system.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
tickets: List of ticket dictionaries
|
|
143
|
+
"""
|
|
144
|
+
try:
|
|
145
|
+
# Initialize ticket manager if needed
|
|
146
|
+
if not hasattr(self, 'ticket_manager'):
|
|
147
|
+
self.ticket_manager = TicketManager()
|
|
148
|
+
|
|
149
|
+
for ticket in tickets:
|
|
150
|
+
try:
|
|
151
|
+
# Create ticket
|
|
152
|
+
ticket_id = await self.ticket_manager.create_ticket(
|
|
153
|
+
title=ticket.get('title', 'Untitled'),
|
|
154
|
+
description=ticket.get('description', ''),
|
|
155
|
+
priority=ticket.get('priority', 'normal'),
|
|
156
|
+
ticket_type=ticket.get('type', 'task'),
|
|
157
|
+
metadata=ticket.get('metadata', {})
|
|
158
|
+
)
|
|
159
|
+
logger.info(f"Created ticket {ticket_id}: {ticket['title']}")
|
|
160
|
+
except Exception as e:
|
|
161
|
+
logger.error(f"Failed to create ticket: {e}")
|
|
162
|
+
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.error(f"Error creating tickets: {e}")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# Example usage
|
|
168
|
+
async def main():
|
|
169
|
+
"""Example usage of hook-enabled orchestrator."""
|
|
170
|
+
# Create orchestrator
|
|
171
|
+
orchestrator = HookEnabledOrchestrator()
|
|
172
|
+
|
|
173
|
+
# Process some prompts
|
|
174
|
+
prompts = [
|
|
175
|
+
"URGENT: Fix the bug in the login system",
|
|
176
|
+
"TODO: Add unit tests for the new API endpoints",
|
|
177
|
+
"Can you create ticket: Implement user dashboard feature",
|
|
178
|
+
"Research the best practices for React performance optimization"
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
for prompt in prompts:
|
|
182
|
+
print(f"\nProcessing: {prompt}")
|
|
183
|
+
response = await orchestrator.process_prompt(prompt)
|
|
184
|
+
print(f"Response: {response[:100]}...")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if __name__ == "__main__":
|
|
188
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Example of how to integrate hooks into orchestrator methods."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from ..hooks.hook_client import HookServiceClient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HookIntegrationExample:
|
|
8
|
+
"""Example methods showing how to integrate hooks into orchestrators."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, hook_client: Optional[HookServiceClient] = None):
|
|
11
|
+
"""Initialize with optional hook client."""
|
|
12
|
+
self.hook_client = hook_client
|
|
13
|
+
|
|
14
|
+
def process_user_input_with_hooks(self, user_input: str) -> str:
|
|
15
|
+
"""Process user input through submit hooks before sending to Claude.
|
|
16
|
+
|
|
17
|
+
This method shows how to:
|
|
18
|
+
1. Send user input to hook service
|
|
19
|
+
2. Get modified input back
|
|
20
|
+
3. Use the modified input for Claude
|
|
21
|
+
"""
|
|
22
|
+
if not self.hook_client:
|
|
23
|
+
# No hooks available, return original input
|
|
24
|
+
return user_input
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
# Execute submit hooks
|
|
28
|
+
results = self.hook_client.execute_submit_hook(
|
|
29
|
+
prompt=user_input,
|
|
30
|
+
session_id="current_session"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Get modified data from hooks
|
|
34
|
+
modified_data = self.hook_client.get_modified_data(results)
|
|
35
|
+
|
|
36
|
+
# Use modified prompt if available
|
|
37
|
+
if modified_data.get('prompt'):
|
|
38
|
+
return modified_data['prompt']
|
|
39
|
+
|
|
40
|
+
# Check for priority changes
|
|
41
|
+
if modified_data.get('priority'):
|
|
42
|
+
# Could adjust Claude model or other settings based on priority
|
|
43
|
+
print(f"Priority set by hooks: {modified_data['priority']}")
|
|
44
|
+
|
|
45
|
+
return user_input
|
|
46
|
+
|
|
47
|
+
except Exception as e:
|
|
48
|
+
print(f"Hook processing failed: {e}")
|
|
49
|
+
# Fallback to original input
|
|
50
|
+
return user_input
|
|
51
|
+
|
|
52
|
+
def process_agent_delegation_with_hooks(self, agent_name: str, task: str) -> tuple[str, str]:
|
|
53
|
+
"""Process agent delegation through pre-delegation hooks.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Tuple of (agent_name, task) potentially modified by hooks
|
|
57
|
+
"""
|
|
58
|
+
if not self.hook_client:
|
|
59
|
+
return agent_name, task
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
# Execute pre-delegation hooks
|
|
63
|
+
context = {
|
|
64
|
+
'agent': agent_name,
|
|
65
|
+
'task': task
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
results = self.hook_client.execute_pre_delegation_hook(
|
|
69
|
+
agent=agent_name,
|
|
70
|
+
context=context
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Get modified data
|
|
74
|
+
modified_data = self.hook_client.get_modified_data(results)
|
|
75
|
+
|
|
76
|
+
# Update agent or task if modified
|
|
77
|
+
new_agent = modified_data.get('agent', agent_name)
|
|
78
|
+
new_context = modified_data.get('context', {})
|
|
79
|
+
new_task = new_context.get('task', task)
|
|
80
|
+
|
|
81
|
+
return new_agent, new_task
|
|
82
|
+
|
|
83
|
+
except Exception as e:
|
|
84
|
+
print(f"Pre-delegation hook failed: {e}")
|
|
85
|
+
return agent_name, task
|
|
86
|
+
|
|
87
|
+
def process_agent_response_with_hooks(self, agent_name: str, response: str) -> str:
|
|
88
|
+
"""Process agent response through post-delegation hooks.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Potentially modified response
|
|
92
|
+
"""
|
|
93
|
+
if not self.hook_client:
|
|
94
|
+
return response
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
# Execute post-delegation hooks
|
|
98
|
+
result = {
|
|
99
|
+
'response': response,
|
|
100
|
+
'success': True
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
results = self.hook_client.execute_post_delegation_hook(
|
|
104
|
+
agent=agent_name,
|
|
105
|
+
result=result
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Get modified data
|
|
109
|
+
modified_data = self.hook_client.get_modified_data(results)
|
|
110
|
+
|
|
111
|
+
# Use modified response if available
|
|
112
|
+
if modified_data.get('result', {}).get('response'):
|
|
113
|
+
return modified_data['result']['response']
|
|
114
|
+
|
|
115
|
+
return response
|
|
116
|
+
|
|
117
|
+
except Exception as e:
|
|
118
|
+
print(f"Post-delegation hook failed: {e}")
|
|
119
|
+
return response
|
|
120
|
+
|
|
121
|
+
def extract_tickets_with_hooks(self, conversation: str) -> list:
|
|
122
|
+
"""Extract tickets from conversation using ticket extraction hooks.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
List of extracted tickets
|
|
126
|
+
"""
|
|
127
|
+
if not self.hook_client:
|
|
128
|
+
return []
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
# Execute ticket extraction hooks
|
|
132
|
+
results = self.hook_client.execute_ticket_extraction_hook(
|
|
133
|
+
content={'conversation': conversation}
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Get extracted tickets
|
|
137
|
+
tickets = self.hook_client.get_extracted_tickets(results)
|
|
138
|
+
|
|
139
|
+
return tickets
|
|
140
|
+
|
|
141
|
+
except Exception as e:
|
|
142
|
+
print(f"Ticket extraction hook failed: {e}")
|
|
143
|
+
return []
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# Example of integrating into an existing orchestrator method
|
|
147
|
+
def example_orchestrator_run_method(self, user_input: str):
|
|
148
|
+
"""Example of how to modify an existing orchestrator run method."""
|
|
149
|
+
|
|
150
|
+
# Process input through hooks if available
|
|
151
|
+
if self.hook_client:
|
|
152
|
+
user_input = self.process_user_input_with_hooks(user_input)
|
|
153
|
+
|
|
154
|
+
# Continue with normal orchestration
|
|
155
|
+
# ... existing orchestrator code ...
|
|
156
|
+
|
|
157
|
+
# When delegating to agents
|
|
158
|
+
if needs_delegation:
|
|
159
|
+
agent_name = "Engineer"
|
|
160
|
+
task = "Implement the feature"
|
|
161
|
+
|
|
162
|
+
# Process through pre-delegation hooks
|
|
163
|
+
if self.hook_client:
|
|
164
|
+
agent_name, task = self.process_agent_delegation_with_hooks(agent_name, task)
|
|
165
|
+
|
|
166
|
+
# Perform delegation
|
|
167
|
+
response = self.delegate_to_agent(agent_name, task)
|
|
168
|
+
|
|
169
|
+
# Process response through post-delegation hooks
|
|
170
|
+
if self.hook_client:
|
|
171
|
+
response = self.process_agent_response_with_hooks(agent_name, response)
|
|
172
|
+
|
|
173
|
+
# Extract tickets from the conversation
|
|
174
|
+
if self.hook_client:
|
|
175
|
+
conversation = f"User: {user_input}\nAssistant: {response}"
|
|
176
|
+
tickets = self.extract_tickets_with_hooks(conversation)
|
|
177
|
+
for ticket in tickets:
|
|
178
|
+
print(f"Extracted ticket: {ticket['title']}")
|