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
claude_mpm/init.py ADDED
@@ -0,0 +1,246 @@
1
+ """Initialization module for claude-mpm.
2
+
3
+ Handles creation of necessary directories and configuration files.
4
+ """
5
+
6
+ import os
7
+ import shutil
8
+ from pathlib import Path
9
+ from typing import Optional, Dict, Any
10
+ import json
11
+
12
+ from claude_mpm.core.logger import get_logger
13
+
14
+
15
+ class ProjectInitializer:
16
+ """Handles initialization of claude-mpm directories and configuration."""
17
+
18
+ def __init__(self):
19
+ self.logger = get_logger("initializer")
20
+ self.user_dir = Path.home() / ".claude-mpm"
21
+ self.project_dir = None
22
+
23
+ def initialize_user_directory(self) -> bool:
24
+ """Initialize user-level .claude-mpm directory structure.
25
+
26
+ Creates:
27
+ - ~/.claude-mpm/
28
+ - agents/
29
+ - user-defined/
30
+ - config/
31
+ - logs/
32
+ - templates/
33
+ """
34
+ try:
35
+ # Create main user directory
36
+ self.user_dir.mkdir(exist_ok=True)
37
+
38
+ # Create subdirectories
39
+ directories = [
40
+ self.user_dir / "agents" / "user-defined",
41
+ self.user_dir / "config",
42
+ self.user_dir / "logs",
43
+ self.user_dir / "templates",
44
+ ]
45
+
46
+ for directory in directories:
47
+ directory.mkdir(parents=True, exist_ok=True)
48
+
49
+ # Create default configuration if it doesn't exist
50
+ config_file = self.user_dir / "config" / "settings.json"
51
+ if not config_file.exists():
52
+ self._create_default_config(config_file)
53
+
54
+ # Copy agent templates if they don't exist
55
+ self._copy_agent_templates()
56
+
57
+ self.logger.info(f"Initialized user directory at {self.user_dir}")
58
+ return True
59
+
60
+ except Exception as e:
61
+ self.logger.error(f"Failed to initialize user directory: {e}")
62
+ return False
63
+
64
+ def initialize_project_directory(self, project_path: Optional[Path] = None) -> bool:
65
+ """Initialize project-level .claude-mpm directory structure.
66
+
67
+ Creates:
68
+ - .claude-mpm/
69
+ - agents/
70
+ - project-specific/
71
+ - config/
72
+ - logs/
73
+ """
74
+ try:
75
+ # Find project root
76
+ if project_path:
77
+ self.project_dir = project_path / ".claude-mpm"
78
+ else:
79
+ project_root = self._find_project_root()
80
+ if not project_root:
81
+ project_root = Path.cwd()
82
+ self.project_dir = project_root / ".claude-mpm"
83
+
84
+ # Create project directory
85
+ self.project_dir.mkdir(exist_ok=True)
86
+
87
+ # Create subdirectories
88
+ directories = [
89
+ self.project_dir / "agents" / "project-specific",
90
+ self.project_dir / "config",
91
+ self.project_dir / "logs",
92
+ ]
93
+
94
+ for directory in directories:
95
+ directory.mkdir(parents=True, exist_ok=True)
96
+
97
+ # Create project configuration
98
+ config_file = self.project_dir / "config" / "project.json"
99
+ if not config_file.exists():
100
+ self._create_project_config(config_file)
101
+
102
+ # Create .gitignore for project directory
103
+ gitignore = self.project_dir / ".gitignore"
104
+ if not gitignore.exists():
105
+ gitignore.write_text("logs/\n*.log\n*.pyc\n__pycache__/\n")
106
+
107
+ self.logger.info(f"Initialized project directory at {self.project_dir}")
108
+ return True
109
+
110
+ except Exception as e:
111
+ self.logger.error(f"Failed to initialize project directory: {e}")
112
+ return False
113
+
114
+ def _find_project_root(self) -> Optional[Path]:
115
+ """Find project root by looking for .git or other project markers."""
116
+ current = Path.cwd()
117
+ while current != current.parent:
118
+ if (current / ".git").exists():
119
+ return current
120
+ if (current / "pyproject.toml").exists():
121
+ return current
122
+ if (current / "setup.py").exists():
123
+ return current
124
+ current = current.parent
125
+ return None
126
+
127
+ def _create_default_config(self, config_file: Path):
128
+ """Create default user configuration."""
129
+ default_config = {
130
+ "version": "1.0",
131
+ "hooks": {
132
+ "enabled": True,
133
+ "port_range": [8080, 8099]
134
+ },
135
+ "logging": {
136
+ "level": "INFO",
137
+ "max_size_mb": 100,
138
+ "retention_days": 30
139
+ },
140
+ "agents": {
141
+ "auto_discover": True,
142
+ "precedence": ["project", "user", "system"]
143
+ },
144
+ "orchestration": {
145
+ "default_mode": "subprocess",
146
+ "enable_todo_hijacking": False
147
+ }
148
+ }
149
+
150
+ with open(config_file, 'w') as f:
151
+ json.dump(default_config, f, indent=2)
152
+
153
+ def _create_project_config(self, config_file: Path):
154
+ """Create default project configuration."""
155
+ project_config = {
156
+ "version": "1.0",
157
+ "project_name": Path.cwd().name,
158
+ "agents": {
159
+ "enabled": True
160
+ },
161
+ "tickets": {
162
+ "auto_create": True,
163
+ "prefix": "TSK"
164
+ }
165
+ }
166
+
167
+ with open(config_file, 'w') as f:
168
+ json.dump(project_config, f, indent=2)
169
+
170
+ def _copy_agent_templates(self):
171
+ """Copy agent templates to user directory."""
172
+ # Get the package directory
173
+ package_dir = Path(__file__).parent
174
+ templates_src = package_dir / "agents" / "templates"
175
+ templates_dst = self.user_dir / "templates"
176
+
177
+ if templates_src.exists():
178
+ for template_file in templates_src.glob("*.md"):
179
+ dst_file = templates_dst / template_file.name
180
+ if not dst_file.exists():
181
+ shutil.copy2(template_file, dst_file)
182
+
183
+ def validate_dependencies(self) -> Dict[str, bool]:
184
+ """Validate that all required dependencies are available."""
185
+ dependencies = {}
186
+
187
+ # Check Python version
188
+ import sys
189
+ dependencies['python'] = sys.version_info >= (3, 8)
190
+
191
+ # Check Claude CLI
192
+ dependencies['claude_cli'] = shutil.which("claude") is not None
193
+
194
+ # Check required Python packages
195
+ required_packages = [
196
+ 'ai_trackdown_pytools',
197
+ 'yaml',
198
+ 'dotenv',
199
+ 'rich',
200
+ 'click',
201
+ 'pexpect',
202
+ 'psutil',
203
+ 'requests',
204
+ 'flask',
205
+ 'watchdog',
206
+ 'tree_sitter'
207
+ ]
208
+
209
+ for package in required_packages:
210
+ try:
211
+ __import__(package)
212
+ dependencies[package] = True
213
+ except ImportError:
214
+ dependencies[package] = False
215
+
216
+ return dependencies
217
+
218
+ def ensure_initialized(self) -> bool:
219
+ """Ensure both user and project directories are initialized."""
220
+ user_ok = self.initialize_user_directory()
221
+ project_ok = self.initialize_project_directory()
222
+ return user_ok and project_ok
223
+
224
+
225
+ def ensure_directories():
226
+ """Convenience function to ensure directories are initialized."""
227
+ initializer = ProjectInitializer()
228
+ return initializer.ensure_initialized()
229
+
230
+
231
+ def validate_installation():
232
+ """Validate that claude-mpm is properly installed."""
233
+ initializer = ProjectInitializer()
234
+ deps = initializer.validate_dependencies()
235
+
236
+ all_ok = all(deps.values())
237
+
238
+ if not all_ok:
239
+ print("❌ Missing dependencies:")
240
+ for dep, status in deps.items():
241
+ if not status:
242
+ print(f" - {dep}")
243
+ else:
244
+ print("✅ All dependencies are installed")
245
+
246
+ return all_ok
@@ -0,0 +1,66 @@
1
+ # Subprocess Orchestration Design
2
+
3
+ ## Problem
4
+ Claude's `--print` mode times out when generating code or using tools, making subprocess orchestration in non-interactive mode impractical.
5
+
6
+ ## Findings
7
+
8
+ ### Interactive Mode (Working)
9
+ - Claude uses built-in Task tool
10
+ - Creates real subprocesses with ~11.4k tokens each
11
+ - Runs in parallel with independent timing
12
+ - Each subprocess gets framework context
13
+
14
+ ### Non-Interactive Mode Issues
15
+ - `claude --print` works for simple queries (e.g., "What is 2+2?" in ~4s)
16
+ - Times out for any code generation or complex tasks
17
+ - Debug shows Claude is working but tool usage adds overhead
18
+ - Requires `--dangerously-skip-permissions` flag to run
19
+
20
+ ## Alternative Approaches
21
+
22
+ ### 1. Use Claude's Conversation API
23
+ Instead of `--print`, use conversation management:
24
+ ```bash
25
+ # Start conversation
26
+ claude --model opus -c "conversation_id" < prompt.txt
27
+
28
+ # Continue conversation
29
+ claude --continue conversation_id < next_prompt.txt
30
+ ```
31
+
32
+ ### 2. Use Interactive Mode with Expect
33
+ Use expect/pexpect to control interactive Claude sessions programmatically.
34
+
35
+ ### 3. Mock Subprocess Mode
36
+ For testing/development:
37
+ - Detect delegations in PM response
38
+ - Show subprocess-like output
39
+ - But don't actually create subprocesses
40
+
41
+ ### 4. Direct API Integration
42
+ Skip CLI entirely and use Claude's API directly (if available).
43
+
44
+ ## Implementation Status
45
+
46
+ ### Completed
47
+ 1. ✅ SubprocessOrchestrator class with full functionality
48
+ 2. ✅ Delegation detection for multiple formats
49
+ 3. ✅ Parallel subprocess execution framework
50
+ 4. ✅ Agent-specific prompt generation
51
+ 5. ✅ CLI integration via `--subprocess` flag
52
+ 6. ✅ Fixed command flags for permissions
53
+
54
+ ### Current Status
55
+ - Implementation is complete but blocked by Claude CLI limitations
56
+ - Use interactive mode for real subprocess orchestration
57
+ - Keep implementation for future when Claude print mode improves
58
+
59
+ ## Delegation Detection Patterns
60
+
61
+ The PM uses these formats:
62
+ - `**Engineer Agent**: Create a function...`
63
+ - `**QA**: Write tests for...`
64
+ - `I'll delegate this to the Engineer agent...`
65
+
66
+ We can parse these and show subprocess-style output even without real subprocesses.
@@ -0,0 +1,6 @@
1
+ """Orchestration components for Claude MPM (legacy)."""
2
+
3
+ # Most orchestration components have been simplified and moved to core.simple_runner
4
+ # This module is kept for backwards compatibility
5
+
6
+ __all__ = []
@@ -0,0 +1,195 @@
1
+ """Direct orchestrator that runs Claude with minimal intervention."""
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
+ 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 DirectOrchestrator:
25
+ """Orchestrator that runs Claude directly with framework injection via file."""
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 Direct Orchestrator (log_level={log_level})")
42
+ else:
43
+ # Minimal logger
44
+ self.logger = get_logger("direct_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 by launching Claude directly."""
61
+ print("Claude MPM Interactive Session")
62
+ print("Framework will be injected on first interaction")
63
+ print("-" * 50)
64
+
65
+ # Get framework instructions
66
+ framework = self.framework_loader.get_framework_instructions()
67
+
68
+ # Save framework to a temporary file
69
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
70
+ f.write(framework)
71
+ f.write("\n\nNOTE: This is the claude-mpm framework. Please acknowledge you've received these instructions and then we can begin our session.\n")
72
+ framework_file = f.name
73
+
74
+ try:
75
+ # Log framework injection
76
+ if self.log_level != "OFF":
77
+ self.logger.info(f"Framework saved to temporary file: {framework_file}")
78
+
79
+ # Also save to prompts directory
80
+ prompt_path = Path.home() / ".claude-mpm" / "prompts"
81
+ prompt_path.mkdir(parents=True, exist_ok=True)
82
+ prompt_file = prompt_path / f"prompt_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
83
+ prompt_file.write_text(framework)
84
+ self.logger.info(f"Framework also saved to: {prompt_file}")
85
+
86
+ # Read the framework content
87
+ with open(framework_file, 'r') as f:
88
+ framework_content = f.read()
89
+
90
+ # Generate a unique session ID
91
+ import uuid
92
+ session_id = str(uuid.uuid4())
93
+
94
+ # Build command to start Claude with print mode and session ID
95
+ cmd = [
96
+ "claude",
97
+ "--model", "opus",
98
+ "--dangerously-skip-permissions",
99
+ "--session-id", session_id,
100
+ "--print", # Print mode
101
+ framework_content
102
+ ]
103
+
104
+ self.logger.info(f"Starting Claude with framework injection (session: {session_id})")
105
+
106
+ # Run Claude with framework
107
+ print("\nInjecting framework instructions...")
108
+ result = self.subprocess_runner.run(cmd)
109
+
110
+ if result.success:
111
+ print("\nFramework injected. Claude's response:")
112
+ print("-" * 50)
113
+ print(result.stdout)
114
+ print("-" * 50)
115
+
116
+ # Debug: show stderr if logging is enabled
117
+ if self.log_level != "OFF" and result.stderr:
118
+ self.logger.debug(f"Claude stderr: {result.stderr}")
119
+
120
+ # Check if we can find the conversation file
121
+ # Parse stderr for conversation file location
122
+ conversation_file = None
123
+ if result.stderr:
124
+ import re
125
+ # Look for patterns like "Conversation saved to: /path/to/file"
126
+ match = re.search(r'(?:conversation saved to|saved to)[:\s]+([^\s]+)', result.stderr, re.I)
127
+ if match:
128
+ conversation_file = match.group(1).strip()
129
+ self.logger.info(f"Found conversation file: {conversation_file}")
130
+
131
+ # Now start interactive Claude session with same session ID
132
+ print("\nStarting interactive session...")
133
+ interactive_cmd = [
134
+ "claude",
135
+ "--model", "opus",
136
+ "--dangerously-skip-permissions"
137
+ ]
138
+
139
+ # Try to continue the conversation
140
+ if conversation_file and Path(conversation_file).exists():
141
+ interactive_cmd.extend(["--continue", conversation_file])
142
+ print(f"Continuing conversation from: {conversation_file}")
143
+ elif session_id:
144
+ interactive_cmd.extend(["--session-id", session_id])
145
+ print(f"Using session ID: {session_id}")
146
+
147
+ # Run Claude interactively
148
+ self.subprocess_runner.run(interactive_cmd)
149
+ else:
150
+ print(f"Error injecting framework: {result.stderr}")
151
+
152
+ self.logger.info(f"Claude exited with code: {result.returncode}")
153
+
154
+ finally:
155
+ # Clean up temporary file
156
+ try:
157
+ os.unlink(framework_file)
158
+ except:
159
+ pass
160
+
161
+ # Ticket creation removed from project
162
+
163
+ # _create_tickets method removed - TicketExtractor functionality removed from project
164
+
165
+ def run_non_interactive(self, user_input: str):
166
+ """Run a non-interactive session using print mode."""
167
+ try:
168
+ # Prepare message with framework
169
+ framework = self.framework_loader.get_framework_instructions()
170
+ full_message = framework + "\n\nUser: " + user_input
171
+
172
+ # Build command
173
+ cmd = [
174
+ "claude",
175
+ "--model", "opus",
176
+ "--dangerously-skip-permissions",
177
+ "--print", # Print mode
178
+ full_message
179
+ ]
180
+
181
+ # Run Claude
182
+ result = self.subprocess_runner.run(cmd)
183
+
184
+ if result.success:
185
+ print(result.stdout)
186
+
187
+ # Ticket extraction removed from project
188
+ else:
189
+ print(f"Error: {result.stderr}")
190
+
191
+ # Ticket creation removed from project
192
+
193
+ except Exception as e:
194
+ print(f"Error: {e}")
195
+ self.logger.error(f"Non-interactive error: {e}")