claude-mpm 3.4.27__py3-none-any.whl → 3.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/INSTRUCTIONS.md +182 -299
  3. claude_mpm/agents/agent_loader.py +283 -57
  4. claude_mpm/agents/agent_loader_integration.py +6 -9
  5. claude_mpm/agents/base_agent.json +2 -1
  6. claude_mpm/agents/base_agent_loader.py +1 -1
  7. claude_mpm/cli/__init__.py +5 -7
  8. claude_mpm/cli/commands/__init__.py +0 -2
  9. claude_mpm/cli/commands/agents.py +1 -1
  10. claude_mpm/cli/commands/memory.py +1 -1
  11. claude_mpm/cli/commands/run.py +12 -0
  12. claude_mpm/cli/parser.py +0 -13
  13. claude_mpm/cli/utils.py +1 -1
  14. claude_mpm/config/__init__.py +44 -2
  15. claude_mpm/config/agent_config.py +348 -0
  16. claude_mpm/config/paths.py +322 -0
  17. claude_mpm/constants.py +0 -1
  18. claude_mpm/core/__init__.py +2 -5
  19. claude_mpm/core/agent_registry.py +63 -17
  20. claude_mpm/core/claude_runner.py +354 -43
  21. claude_mpm/core/config.py +7 -1
  22. claude_mpm/core/config_aliases.py +4 -3
  23. claude_mpm/core/config_paths.py +151 -0
  24. claude_mpm/core/factories.py +4 -50
  25. claude_mpm/core/logger.py +11 -13
  26. claude_mpm/core/service_registry.py +2 -2
  27. claude_mpm/dashboard/static/js/components/agent-inference.js +101 -25
  28. claude_mpm/dashboard/static/js/components/event-processor.js +3 -2
  29. claude_mpm/hooks/claude_hooks/hook_handler.py +343 -83
  30. claude_mpm/hooks/memory_integration_hook.py +1 -1
  31. claude_mpm/init.py +37 -6
  32. claude_mpm/scripts/socketio_daemon.py +6 -2
  33. claude_mpm/services/__init__.py +71 -3
  34. claude_mpm/services/agents/__init__.py +85 -0
  35. claude_mpm/services/agents/deployment/__init__.py +21 -0
  36. claude_mpm/services/{agent_deployment.py → agents/deployment/agent_deployment.py} +192 -41
  37. claude_mpm/services/{agent_lifecycle_manager.py → agents/deployment/agent_lifecycle_manager.py} +11 -10
  38. claude_mpm/services/agents/loading/__init__.py +11 -0
  39. claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +9 -8
  40. claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +2 -2
  41. claude_mpm/services/{framework_agent_loader.py → agents/loading/framework_agent_loader.py} +116 -40
  42. claude_mpm/services/agents/management/__init__.py +9 -0
  43. claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +6 -5
  44. claude_mpm/services/agents/memory/__init__.py +21 -0
  45. claude_mpm/services/{agent_memory_manager.py → agents/memory/agent_memory_manager.py} +3 -3
  46. claude_mpm/services/agents/registry/__init__.py +29 -0
  47. claude_mpm/services/{agent_registry.py → agents/registry/agent_registry.py} +101 -16
  48. claude_mpm/services/{deployed_agent_discovery.py → agents/registry/deployed_agent_discovery.py} +12 -2
  49. claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +6 -5
  50. claude_mpm/services/async_session_logger.py +584 -0
  51. claude_mpm/services/claude_session_logger.py +299 -0
  52. claude_mpm/services/framework_claude_md_generator/content_assembler.py +2 -2
  53. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +17 -17
  54. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +3 -3
  55. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +1 -1
  56. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +1 -1
  57. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +19 -24
  58. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +1 -1
  59. claude_mpm/services/framework_claude_md_generator.py +4 -2
  60. claude_mpm/services/memory/__init__.py +17 -0
  61. claude_mpm/services/{memory_builder.py → memory/builder.py} +3 -3
  62. claude_mpm/services/memory/cache/__init__.py +14 -0
  63. claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +1 -1
  64. claude_mpm/services/memory/cache/simple_cache.py +317 -0
  65. claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +1 -1
  66. claude_mpm/services/{memory_router.py → memory/router.py} +1 -1
  67. claude_mpm/services/optimized_hook_service.py +542 -0
  68. claude_mpm/services/project_registry.py +14 -8
  69. claude_mpm/services/response_tracker.py +237 -0
  70. claude_mpm/services/ticketing_service_original.py +4 -2
  71. claude_mpm/services/version_control/branch_strategy.py +3 -1
  72. claude_mpm/utils/paths.py +12 -10
  73. claude_mpm/utils/session_logging.py +114 -0
  74. claude_mpm/validation/agent_validator.py +2 -1
  75. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/METADATA +26 -20
  76. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/RECORD +83 -106
  77. claude_mpm/cli/commands/ui.py +0 -57
  78. claude_mpm/core/simple_runner.py +0 -1046
  79. claude_mpm/hooks/builtin/__init__.py +0 -1
  80. claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
  81. claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
  82. claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
  83. claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
  84. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
  85. claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
  86. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
  87. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
  88. claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
  89. claude_mpm/orchestration/__init__.py +0 -6
  90. claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
  91. claude_mpm/orchestration/archive/factory.py +0 -215
  92. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
  93. claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
  94. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
  95. claude_mpm/orchestration/archive/orchestrator.py +0 -501
  96. claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
  97. claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
  98. claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
  99. claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
  100. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
  101. claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
  102. claude_mpm/schemas/workflow_validator.py +0 -411
  103. claude_mpm/services/parent_directory_manager/__init__.py +0 -577
  104. claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
  105. claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
  106. claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
  107. claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
  108. claude_mpm/services/parent_directory_manager/operations.py +0 -186
  109. claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
  110. claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
  111. claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
  112. claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
  113. claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
  114. claude_mpm/ui/__init__.py +0 -1
  115. claude_mpm/ui/rich_terminal_ui.py +0 -295
  116. claude_mpm/ui/terminal_ui.py +0 -328
  117. /claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +0 -0
  118. /claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +0 -0
  119. /claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +0 -0
  120. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/WHEEL +0 -0
  121. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/entry_points.txt +0 -0
  122. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/licenses/LICENSE +0 -0
  123. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.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}")