claude-mpm 3.4.27__py3-none-any.whl → 3.5.1__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.1.dist-info}/METADATA +28 -20
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.1.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.1.dist-info}/WHEEL +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.1.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.1.dist-info}/top_level.txt +0 -0
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
"""Orchestrator using Claude's system prompt feature."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import Optional
|
|
6
|
-
from datetime import datetime
|
|
7
|
-
import logging
|
|
8
|
-
import tempfile
|
|
9
|
-
|
|
10
|
-
try:
|
|
11
|
-
from ..core.logger import get_logger, setup_logging
|
|
12
|
-
from ..utils.subprocess_runner import SubprocessRunner
|
|
13
|
-
# TicketExtractor removed from project
|
|
14
|
-
from ..core.framework_loader import FrameworkLoader
|
|
15
|
-
from .agent_delegator import AgentDelegator
|
|
16
|
-
# Hook client removed - will use JSON-RPC client from hook manager
|
|
17
|
-
except ImportError:
|
|
18
|
-
from core.logger import get_logger, setup_logging
|
|
19
|
-
from utils.subprocess_runner import SubprocessRunner
|
|
20
|
-
# TicketExtractor removed from project
|
|
21
|
-
from core.framework_loader import FrameworkLoader
|
|
22
|
-
from orchestration.agent_delegator import AgentDelegator
|
|
23
|
-
# Hook client removed - will use JSON-RPC client from hook manager
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class SystemPromptOrchestrator:
|
|
27
|
-
"""Orchestrator that uses Claude's --system-prompt or --append-system-prompt."""
|
|
28
|
-
|
|
29
|
-
def __init__(
|
|
30
|
-
self,
|
|
31
|
-
framework_path: Optional[Path] = None,
|
|
32
|
-
agents_dir: Optional[Path] = None,
|
|
33
|
-
log_level: str = "OFF",
|
|
34
|
-
log_dir: Optional[Path] = None,
|
|
35
|
-
hook_manager=None,
|
|
36
|
-
):
|
|
37
|
-
"""Initialize the orchestrator."""
|
|
38
|
-
self.log_level = log_level
|
|
39
|
-
self.log_dir = log_dir or (Path.home() / ".claude-mpm" / "logs")
|
|
40
|
-
self.hook_manager = hook_manager
|
|
41
|
-
|
|
42
|
-
# Set up logging
|
|
43
|
-
if log_level != "OFF":
|
|
44
|
-
self.logger = setup_logging(level=log_level, log_dir=log_dir)
|
|
45
|
-
self.logger.info(f"Initializing System Prompt Orchestrator (log_level={log_level})")
|
|
46
|
-
if hook_manager and hook_manager.is_available():
|
|
47
|
-
self.logger.info(f"Hook service available on port {hook_manager.port}")
|
|
48
|
-
else:
|
|
49
|
-
# Minimal logger
|
|
50
|
-
self.logger = get_logger("system_prompt_orchestrator")
|
|
51
|
-
self.logger.setLevel(logging.WARNING)
|
|
52
|
-
|
|
53
|
-
# Components
|
|
54
|
-
self.framework_loader = FrameworkLoader(framework_path, agents_dir)
|
|
55
|
-
# TicketExtractor removed from project
|
|
56
|
-
self.agent_delegator = AgentDelegator(self.framework_loader.agent_registry)
|
|
57
|
-
|
|
58
|
-
# Initialize hook client if available
|
|
59
|
-
self.hook_client = None
|
|
60
|
-
if self.hook_manager and self.hook_manager.is_available():
|
|
61
|
-
try:
|
|
62
|
-
self.hook_client = self.hook_manager.get_client()
|
|
63
|
-
if self.hook_client:
|
|
64
|
-
health = self.hook_client.health_check()
|
|
65
|
-
if health.get('status') == 'healthy':
|
|
66
|
-
self.logger.info(f"Using JSON-RPC hook client with {health.get('hook_count', 0)} hooks")
|
|
67
|
-
else:
|
|
68
|
-
self.logger.warning("Hook client not healthy, disabling hooks")
|
|
69
|
-
self.hook_client = None
|
|
70
|
-
except Exception as e:
|
|
71
|
-
self.logger.warning(f"Failed to get hook client: {e}")
|
|
72
|
-
self.hook_client = None
|
|
73
|
-
|
|
74
|
-
# State
|
|
75
|
-
self.session_start = datetime.now()
|
|
76
|
-
# Ticket creation removed from project
|
|
77
|
-
|
|
78
|
-
# Initialize subprocess runner
|
|
79
|
-
self.subprocess_runner = SubprocessRunner(logger=self.logger)
|
|
80
|
-
|
|
81
|
-
def run_interactive(self):
|
|
82
|
-
"""Run an interactive session with framework as system prompt."""
|
|
83
|
-
from claude_mpm._version import __version__
|
|
84
|
-
print(f"Claude MPM v{__version__} - Interactive Session")
|
|
85
|
-
print("Starting Claude with framework system prompt...")
|
|
86
|
-
print("-" * 50)
|
|
87
|
-
|
|
88
|
-
# Get framework instructions
|
|
89
|
-
framework = self.framework_loader.get_framework_instructions()
|
|
90
|
-
|
|
91
|
-
# Submit hook for framework initialization
|
|
92
|
-
if self.hook_client:
|
|
93
|
-
try:
|
|
94
|
-
self.logger.info("Calling submit hook for framework initialization")
|
|
95
|
-
hook_results = self.hook_client.execute_submit_hook(
|
|
96
|
-
prompt="Framework initialization with system prompt",
|
|
97
|
-
framework_length=len(framework),
|
|
98
|
-
session_type="interactive",
|
|
99
|
-
timestamp=datetime.now().isoformat()
|
|
100
|
-
)
|
|
101
|
-
if hook_results:
|
|
102
|
-
self.logger.info(f"Submit hook executed: {len(hook_results)} hooks processed")
|
|
103
|
-
# Check for any modified data
|
|
104
|
-
modified = self.hook_client.get_modified_data(hook_results)
|
|
105
|
-
if modified:
|
|
106
|
-
self.logger.info(f"Submit hook modified data: {modified}")
|
|
107
|
-
except Exception as e:
|
|
108
|
-
self.logger.warning(f"Submit hook error (continuing): {e}")
|
|
109
|
-
|
|
110
|
-
# Save framework to a temporary file (system prompt might be too long for command line)
|
|
111
|
-
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
|
|
112
|
-
f.write(framework)
|
|
113
|
-
framework_file = f.name
|
|
114
|
-
|
|
115
|
-
try:
|
|
116
|
-
# Log framework injection
|
|
117
|
-
if self.log_level != "OFF":
|
|
118
|
-
self.logger.info(f"Framework saved to temporary file: {framework_file}")
|
|
119
|
-
|
|
120
|
-
# Also save to prompts directory
|
|
121
|
-
prompt_path = Path.home() / ".claude-mpm" / "prompts"
|
|
122
|
-
prompt_path.mkdir(parents=True, exist_ok=True)
|
|
123
|
-
prompt_file = prompt_path / f"prompt_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
|
|
124
|
-
prompt_file.write_text(framework)
|
|
125
|
-
self.logger.info(f"Framework also saved to: {prompt_file}")
|
|
126
|
-
|
|
127
|
-
# Build command for interactive mode
|
|
128
|
-
# For now, just launch Claude directly
|
|
129
|
-
cmd = [
|
|
130
|
-
"claude",
|
|
131
|
-
"--dangerously-skip-permissions"
|
|
132
|
-
]
|
|
133
|
-
|
|
134
|
-
self.logger.info("Starting Claude with framework as system prompt")
|
|
135
|
-
|
|
136
|
-
# Note: In interactive mode, we cannot intercept Task tool delegations
|
|
137
|
-
# as they are handled internally by Claude. Future enhancement could
|
|
138
|
-
# use a different approach like pexpect to monitor the output stream.
|
|
139
|
-
|
|
140
|
-
# Run Claude interactively with framework as system prompt
|
|
141
|
-
# Use subprocess.Popen directly for proper interactive mode
|
|
142
|
-
import subprocess
|
|
143
|
-
self.logger.info(f"Launching Claude interactively with command: {' '.join(cmd)}")
|
|
144
|
-
print(f"Debug: Running command: {' '.join(cmd)}")
|
|
145
|
-
|
|
146
|
-
# Start Claude with direct terminal I/O
|
|
147
|
-
process = subprocess.Popen(cmd)
|
|
148
|
-
|
|
149
|
-
# Wait for Claude to complete
|
|
150
|
-
returncode = process.wait()
|
|
151
|
-
|
|
152
|
-
self.logger.info(f"Claude exited with code: {returncode}")
|
|
153
|
-
|
|
154
|
-
# Post-session hook (no delegations captured in interactive mode)
|
|
155
|
-
if self.hook_client:
|
|
156
|
-
try:
|
|
157
|
-
self.logger.info("Calling post-session hook")
|
|
158
|
-
hook_results = self.hook_client.execute_post_delegation_hook(
|
|
159
|
-
agent="system",
|
|
160
|
-
result={
|
|
161
|
-
"task": "Interactive session completed",
|
|
162
|
-
"exit_code": returncode,
|
|
163
|
-
"session_type": "interactive",
|
|
164
|
-
"note": "Task delegations not captured in interactive mode"
|
|
165
|
-
}
|
|
166
|
-
)
|
|
167
|
-
if hook_results:
|
|
168
|
-
self.logger.info(f"Post-session hook executed: {len(hook_results)} hooks processed")
|
|
169
|
-
# Extract any tickets from hook results
|
|
170
|
-
# Ticket extraction removed from project
|
|
171
|
-
except Exception as e:
|
|
172
|
-
self.logger.warning(f"Post-session hook error: {e}")
|
|
173
|
-
|
|
174
|
-
finally:
|
|
175
|
-
# Clean up temporary file
|
|
176
|
-
try:
|
|
177
|
-
os.unlink(framework_file)
|
|
178
|
-
except:
|
|
179
|
-
pass
|
|
180
|
-
|
|
181
|
-
# Ticket creation removed from project
|
|
182
|
-
|
|
183
|
-
# _create_tickets method removed - TicketExtractor functionality removed from project
|
|
184
|
-
|
|
185
|
-
def run_non_interactive(self, user_input: str):
|
|
186
|
-
"""Run a non-interactive session using print mode."""
|
|
187
|
-
try:
|
|
188
|
-
# Submit hook for user input
|
|
189
|
-
if self.hook_client:
|
|
190
|
-
try:
|
|
191
|
-
self.logger.info("Calling submit hook for user input")
|
|
192
|
-
hook_results = self.hook_client.execute_submit_hook(
|
|
193
|
-
prompt=user_input,
|
|
194
|
-
session_type="non-interactive",
|
|
195
|
-
timestamp=datetime.now().isoformat()
|
|
196
|
-
)
|
|
197
|
-
if hook_results:
|
|
198
|
-
self.logger.info(f"Submit hook executed: {len(hook_results)} hooks processed")
|
|
199
|
-
except Exception as e:
|
|
200
|
-
self.logger.warning(f"Submit hook error (continuing): {e}")
|
|
201
|
-
|
|
202
|
-
# For testing, use a minimal prompt
|
|
203
|
-
# TODO: Re-enable full framework once Claude --print issues are resolved
|
|
204
|
-
minimal_prompt = "You are Claude, an AI assistant."
|
|
205
|
-
|
|
206
|
-
# Log framework size
|
|
207
|
-
if self.log_level != "OFF":
|
|
208
|
-
self.logger.info(f"Using minimal test prompt: {len(minimal_prompt)} chars")
|
|
209
|
-
|
|
210
|
-
full_message = minimal_prompt + "\n\nUser: " + user_input
|
|
211
|
-
|
|
212
|
-
# Build command for non-interactive mode
|
|
213
|
-
cmd = [
|
|
214
|
-
"claude",
|
|
215
|
-
"--dangerously-skip-permissions",
|
|
216
|
-
"--print", # Print response and exit
|
|
217
|
-
full_message
|
|
218
|
-
]
|
|
219
|
-
|
|
220
|
-
# Log command details for debugging
|
|
221
|
-
if self.log_level != "OFF":
|
|
222
|
-
self.logger.debug(f"Command: claude --dangerously-skip-permissions --print <message of {len(full_message)} chars>")
|
|
223
|
-
# Also save message for debugging
|
|
224
|
-
debug_file = Path.home() / ".claude-mpm" / "debug" / f"message_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
|
|
225
|
-
debug_file.parent.mkdir(parents=True, exist_ok=True)
|
|
226
|
-
debug_file.write_text(full_message)
|
|
227
|
-
self.logger.debug(f"Message saved to: {debug_file}")
|
|
228
|
-
|
|
229
|
-
# Run Claude with message as argument
|
|
230
|
-
result = self.subprocess_runner.run_with_timeout(cmd, timeout=60)
|
|
231
|
-
|
|
232
|
-
if result.success:
|
|
233
|
-
print(result.stdout)
|
|
234
|
-
|
|
235
|
-
# Process output for tickets and delegations
|
|
236
|
-
delegations_detected = []
|
|
237
|
-
for line in result.stdout.split('\n'):
|
|
238
|
-
# Ticket extraction removed from project
|
|
239
|
-
|
|
240
|
-
# Extract delegations (for logging, not actual interception)
|
|
241
|
-
delegations = self.agent_delegator.extract_delegations(line)
|
|
242
|
-
delegations_detected.extend(delegations)
|
|
243
|
-
|
|
244
|
-
# Log detected delegations
|
|
245
|
-
if delegations_detected and self.log_level != "OFF":
|
|
246
|
-
self.logger.info(f"Detected {len(delegations_detected)} Task tool delegations")
|
|
247
|
-
for d in delegations_detected:
|
|
248
|
-
self.logger.info(f" - {d['agent']}: {d['task'][:50]}...")
|
|
249
|
-
|
|
250
|
-
# Post-delegation hook with full output
|
|
251
|
-
if self.hook_client:
|
|
252
|
-
try:
|
|
253
|
-
self.logger.info("Calling post-delegation hook for non-interactive output")
|
|
254
|
-
hook_results = self.hook_client.execute_post_delegation_hook(
|
|
255
|
-
agent="system",
|
|
256
|
-
result={
|
|
257
|
-
"task": user_input,
|
|
258
|
-
"output": result.stdout,
|
|
259
|
-
"delegations_detected": len(delegations_detected),
|
|
260
|
-
"session_type": "non-interactive"
|
|
261
|
-
}
|
|
262
|
-
)
|
|
263
|
-
if hook_results:
|
|
264
|
-
self.logger.info(f"Post-delegation hook executed: {len(hook_results)} hooks processed")
|
|
265
|
-
# Ticket extraction removed from project
|
|
266
|
-
except Exception as e:
|
|
267
|
-
self.logger.warning(f"Post-delegation hook error: {e}")
|
|
268
|
-
else:
|
|
269
|
-
if result.timed_out:
|
|
270
|
-
print(f"Error: Command timed out after 60 seconds")
|
|
271
|
-
else:
|
|
272
|
-
print(f"Error: {result.stderr}")
|
|
273
|
-
|
|
274
|
-
# Ticket creation removed from project
|
|
275
|
-
|
|
276
|
-
except Exception as e:
|
|
277
|
-
print(f"Error: {e}")
|
|
278
|
-
self.logger.error(f"Non-interactive error: {e}")
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
"""Wrapper orchestrator that creates a custom Claude wrapper."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import tempfile
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Optional
|
|
7
|
-
from datetime import datetime
|
|
8
|
-
import logging
|
|
9
|
-
|
|
10
|
-
try:
|
|
11
|
-
from ..core.logger import get_logger, setup_logging
|
|
12
|
-
from ..utils.subprocess_runner import SubprocessRunner
|
|
13
|
-
# TicketExtractor removed from project
|
|
14
|
-
from ..core.framework_loader import FrameworkLoader
|
|
15
|
-
from .agent_delegator import AgentDelegator
|
|
16
|
-
except ImportError:
|
|
17
|
-
from core.logger import get_logger, setup_logging
|
|
18
|
-
from utils.subprocess_runner import SubprocessRunner
|
|
19
|
-
# TicketExtractor removed from project
|
|
20
|
-
from core.framework_loader import FrameworkLoader
|
|
21
|
-
from orchestration.agent_delegator import AgentDelegator
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class WrapperOrchestrator:
|
|
25
|
-
"""Orchestrator that creates a wrapper script for Claude with framework."""
|
|
26
|
-
|
|
27
|
-
def __init__(
|
|
28
|
-
self,
|
|
29
|
-
framework_path: Optional[Path] = None,
|
|
30
|
-
agents_dir: Optional[Path] = None,
|
|
31
|
-
log_level: str = "OFF",
|
|
32
|
-
log_dir: Optional[Path] = None,
|
|
33
|
-
):
|
|
34
|
-
"""Initialize the orchestrator."""
|
|
35
|
-
self.log_level = log_level
|
|
36
|
-
self.log_dir = log_dir or (Path.home() / ".claude-mpm" / "logs")
|
|
37
|
-
|
|
38
|
-
# Set up logging
|
|
39
|
-
if log_level != "OFF":
|
|
40
|
-
self.logger = setup_logging(level=log_level, log_dir=log_dir)
|
|
41
|
-
self.logger.info(f"Initializing Wrapper Orchestrator (log_level={log_level})")
|
|
42
|
-
else:
|
|
43
|
-
# Minimal logger
|
|
44
|
-
self.logger = get_logger("wrapper_orchestrator")
|
|
45
|
-
self.logger.setLevel(logging.WARNING)
|
|
46
|
-
|
|
47
|
-
# Components
|
|
48
|
-
self.framework_loader = FrameworkLoader(framework_path, agents_dir)
|
|
49
|
-
# TicketExtractor removed from project
|
|
50
|
-
self.agent_delegator = AgentDelegator(self.framework_loader.agent_registry)
|
|
51
|
-
|
|
52
|
-
# State
|
|
53
|
-
self.session_start = datetime.now()
|
|
54
|
-
# Ticket creation removed from project
|
|
55
|
-
|
|
56
|
-
# Initialize subprocess runner
|
|
57
|
-
self.subprocess_runner = SubprocessRunner(logger=self.logger)
|
|
58
|
-
|
|
59
|
-
def run_interactive(self):
|
|
60
|
-
"""Run an interactive session with a custom wrapper."""
|
|
61
|
-
print("Claude MPM Interactive Session")
|
|
62
|
-
print("Creating framework-aware Claude wrapper...")
|
|
63
|
-
print("-" * 50)
|
|
64
|
-
|
|
65
|
-
# Get framework instructions
|
|
66
|
-
framework = self.framework_loader.get_framework_instructions()
|
|
67
|
-
|
|
68
|
-
# Create a wrapper script
|
|
69
|
-
with tempfile.NamedTemporaryFile(mode='w', suffix='.sh', delete=False) as f:
|
|
70
|
-
wrapper_script = f"""#!/bin/bash
|
|
71
|
-
# Claude MPM Wrapper Script
|
|
72
|
-
# This script ensures framework instructions are included in every conversation
|
|
73
|
-
|
|
74
|
-
# Check if this is the first message
|
|
75
|
-
if [ ! -f "$HOME/.claude-mpm/.framework_injected" ]; then
|
|
76
|
-
# First message - prepend framework
|
|
77
|
-
echo "Injecting framework instructions..."
|
|
78
|
-
|
|
79
|
-
# Create the framework message
|
|
80
|
-
FRAMEWORK_MSG=$(cat << 'EOF_FRAMEWORK'
|
|
81
|
-
{framework}
|
|
82
|
-
|
|
83
|
-
User: $@
|
|
84
|
-
EOF_FRAMEWORK
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
# Mark as injected
|
|
88
|
-
mkdir -p "$HOME/.claude-mpm"
|
|
89
|
-
touch "$HOME/.claude-mpm/.framework_injected"
|
|
90
|
-
|
|
91
|
-
# Run Claude with framework
|
|
92
|
-
exec claude --model opus --dangerously-skip-permissions -p "$FRAMEWORK_MSG"
|
|
93
|
-
else
|
|
94
|
-
# Subsequent messages - just pass through
|
|
95
|
-
exec claude --model opus --dangerously-skip-permissions "$@"
|
|
96
|
-
fi
|
|
97
|
-
"""
|
|
98
|
-
f.write(wrapper_script)
|
|
99
|
-
wrapper_path = f.name
|
|
100
|
-
|
|
101
|
-
try:
|
|
102
|
-
# Make wrapper executable
|
|
103
|
-
os.chmod(wrapper_path, 0o755)
|
|
104
|
-
|
|
105
|
-
# Clean up any previous session marker
|
|
106
|
-
marker_file = Path.home() / ".claude-mpm" / ".framework_injected"
|
|
107
|
-
if marker_file.exists():
|
|
108
|
-
marker_file.unlink()
|
|
109
|
-
|
|
110
|
-
# Log wrapper creation
|
|
111
|
-
if self.log_level != "OFF":
|
|
112
|
-
self.logger.info(f"Created wrapper script: {wrapper_path}")
|
|
113
|
-
|
|
114
|
-
# Save framework to prompts directory
|
|
115
|
-
prompt_path = Path.home() / ".claude-mpm" / "prompts"
|
|
116
|
-
prompt_path.mkdir(parents=True, exist_ok=True)
|
|
117
|
-
prompt_file = prompt_path / f"prompt_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
|
|
118
|
-
prompt_file.write_text(framework)
|
|
119
|
-
self.logger.info(f"Framework saved to: {prompt_file}")
|
|
120
|
-
|
|
121
|
-
print("\nWrapper created. Starting Claude with framework context...")
|
|
122
|
-
print("(Framework will be injected on your first message)")
|
|
123
|
-
print()
|
|
124
|
-
|
|
125
|
-
# Set CLAUDE_WRAPPER environment variable
|
|
126
|
-
env = os.environ.copy()
|
|
127
|
-
env['CLAUDE_WRAPPER'] = wrapper_path
|
|
128
|
-
|
|
129
|
-
# Run Claude with our wrapper in PATH
|
|
130
|
-
# Create a temporary directory for our wrapper
|
|
131
|
-
with tempfile.TemporaryDirectory() as tmpdir:
|
|
132
|
-
# Create symlink to wrapper as 'claude'
|
|
133
|
-
wrapper_link = Path(tmpdir) / "claude"
|
|
134
|
-
wrapper_link.symlink_to(wrapper_path)
|
|
135
|
-
|
|
136
|
-
# Prepend our directory to PATH
|
|
137
|
-
env['PATH'] = f"{tmpdir}:{env['PATH']}"
|
|
138
|
-
|
|
139
|
-
# Run claude (which will use our wrapper)
|
|
140
|
-
self.subprocess_runner.run(["claude"], env=env)
|
|
141
|
-
|
|
142
|
-
finally:
|
|
143
|
-
# Clean up
|
|
144
|
-
try:
|
|
145
|
-
os.unlink(wrapper_path)
|
|
146
|
-
except:
|
|
147
|
-
pass
|
|
148
|
-
|
|
149
|
-
# Clean up marker file
|
|
150
|
-
if marker_file.exists():
|
|
151
|
-
marker_file.unlink()
|
|
152
|
-
|
|
153
|
-
# Ticket creation removed from project
|
|
154
|
-
|
|
155
|
-
# _create_tickets method removed - TicketExtractor functionality removed from project
|
|
156
|
-
|
|
157
|
-
def run_non_interactive(self, user_input: str):
|
|
158
|
-
"""Run a non-interactive session using print mode."""
|
|
159
|
-
try:
|
|
160
|
-
# Prepare message with framework
|
|
161
|
-
framework = self.framework_loader.get_framework_instructions()
|
|
162
|
-
full_message = framework + "\n\nUser: " + user_input
|
|
163
|
-
|
|
164
|
-
# Build command
|
|
165
|
-
cmd = [
|
|
166
|
-
"claude",
|
|
167
|
-
"--model", "opus",
|
|
168
|
-
"--dangerously-skip-permissions",
|
|
169
|
-
"--print", # Print mode
|
|
170
|
-
full_message
|
|
171
|
-
]
|
|
172
|
-
|
|
173
|
-
# Run Claude
|
|
174
|
-
result = self.subprocess_runner.run(cmd)
|
|
175
|
-
|
|
176
|
-
if result.success:
|
|
177
|
-
print(result.stdout)
|
|
178
|
-
|
|
179
|
-
# Ticket extraction removed from project
|
|
180
|
-
else:
|
|
181
|
-
print(f"Error: {result.stderr}")
|
|
182
|
-
|
|
183
|
-
# Ticket creation removed from project
|
|
184
|
-
|
|
185
|
-
except Exception as e:
|
|
186
|
-
print(f"Error: {e}")
|
|
187
|
-
self.logger.error(f"Non-interactive error: {e}")
|