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
claude_mpm/core/claude_runner.py
CHANGED
|
@@ -9,15 +9,16 @@ from datetime import datetime
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from typing import Optional
|
|
11
11
|
import uuid
|
|
12
|
+
from claude_mpm.config.paths import paths
|
|
12
13
|
|
|
13
14
|
try:
|
|
14
|
-
from claude_mpm.services.
|
|
15
|
+
from claude_mpm.services.agents.deployment import AgentDeploymentService
|
|
15
16
|
from claude_mpm.services.ticket_manager import TicketManager
|
|
16
17
|
from claude_mpm.services.hook_service import HookService
|
|
17
18
|
from claude_mpm.core.config import Config
|
|
18
19
|
from claude_mpm.core.logger import get_logger, get_project_logger, ProjectLogger
|
|
19
20
|
except ImportError:
|
|
20
|
-
from claude_mpm.services.
|
|
21
|
+
from claude_mpm.services.agents.deployment import AgentDeploymentService
|
|
21
22
|
from claude_mpm.services.ticket_manager import TicketManager
|
|
22
23
|
from claude_mpm.services.hook_service import HookService
|
|
23
24
|
from claude_mpm.core.config import Config
|
|
@@ -67,23 +68,58 @@ class ClaudeRunner:
|
|
|
67
68
|
level="INFO",
|
|
68
69
|
component="runner"
|
|
69
70
|
)
|
|
71
|
+
except ImportError as e:
|
|
72
|
+
self.logger.warning(f"Project logger module not available: {e}")
|
|
70
73
|
except Exception as e:
|
|
71
74
|
self.logger.warning(f"Failed to initialize project logger: {e}")
|
|
72
75
|
|
|
73
|
-
# Initialize services
|
|
74
|
-
|
|
76
|
+
# Initialize services with proper error handling
|
|
77
|
+
try:
|
|
78
|
+
self.deployment_service = AgentDeploymentService()
|
|
79
|
+
except ImportError as e:
|
|
80
|
+
self.logger.error(f"Failed to import AgentDeploymentService: {e}")
|
|
81
|
+
raise RuntimeError("Required module AgentDeploymentService not available. Please reinstall claude-mpm.") from e
|
|
82
|
+
except Exception as e:
|
|
83
|
+
self.logger.error(f"Failed to initialize AgentDeploymentService: {e}")
|
|
84
|
+
raise RuntimeError(f"Agent deployment service initialization failed: {e}") from e
|
|
85
|
+
|
|
86
|
+
# Initialize ticket manager if enabled
|
|
75
87
|
if enable_tickets:
|
|
76
88
|
try:
|
|
77
89
|
self.ticket_manager = TicketManager()
|
|
78
|
-
except
|
|
79
|
-
self.logger.warning(f"Ticket manager not available: {e}")
|
|
90
|
+
except ImportError as e:
|
|
91
|
+
self.logger.warning(f"Ticket manager module not available: {e}")
|
|
92
|
+
self.ticket_manager = None
|
|
93
|
+
self.enable_tickets = False
|
|
94
|
+
except TypeError as e:
|
|
95
|
+
self.logger.warning(f"Ticket manager initialization error: {e}")
|
|
96
|
+
self.ticket_manager = None
|
|
97
|
+
self.enable_tickets = False
|
|
98
|
+
except Exception as e:
|
|
99
|
+
self.logger.warning(f"Unexpected error initializing ticket manager: {e}")
|
|
80
100
|
self.ticket_manager = None
|
|
81
101
|
self.enable_tickets = False
|
|
82
102
|
|
|
83
|
-
# Initialize
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
103
|
+
# Initialize configuration
|
|
104
|
+
try:
|
|
105
|
+
self.config = Config()
|
|
106
|
+
except FileNotFoundError as e:
|
|
107
|
+
self.logger.warning(f"Configuration file not found, using defaults: {e}")
|
|
108
|
+
self.config = Config() # Will use defaults
|
|
109
|
+
except Exception as e:
|
|
110
|
+
self.logger.error(f"Failed to load configuration: {e}")
|
|
111
|
+
raise RuntimeError(f"Configuration initialization failed: {e}") from e
|
|
112
|
+
|
|
113
|
+
# Initialize hook service
|
|
114
|
+
try:
|
|
115
|
+
self.hook_service = HookService(self.config)
|
|
116
|
+
self._register_memory_hooks()
|
|
117
|
+
except ImportError as e:
|
|
118
|
+
self.logger.warning(f"Hook service module not available: {e}")
|
|
119
|
+
self.hook_service = None
|
|
120
|
+
except Exception as e:
|
|
121
|
+
self.logger.warning(f"Failed to initialize hook service: {e}")
|
|
122
|
+
self.hook_service = None
|
|
87
123
|
|
|
88
124
|
# Load system instructions
|
|
89
125
|
self.system_instructions = self._load_system_instructions()
|
|
@@ -101,6 +137,10 @@ class ClaudeRunner:
|
|
|
101
137
|
"log_level": log_level,
|
|
102
138
|
"launch_method": launch_method
|
|
103
139
|
})
|
|
140
|
+
except PermissionError as e:
|
|
141
|
+
self.logger.debug(f"Permission denied creating session log file: {e}")
|
|
142
|
+
except OSError as e:
|
|
143
|
+
self.logger.debug(f"OS error creating session log file: {e}")
|
|
104
144
|
except Exception as e:
|
|
105
145
|
self.logger.debug(f"Failed to create session log file: {e}")
|
|
106
146
|
|
|
@@ -148,12 +188,99 @@ class ClaudeRunner:
|
|
|
148
188
|
)
|
|
149
189
|
return True
|
|
150
190
|
|
|
191
|
+
|
|
192
|
+
except PermissionError as e:
|
|
193
|
+
error_msg = f"Permission denied deploying agents to .claude/agents/: {e}"
|
|
194
|
+
self.logger.error(error_msg)
|
|
195
|
+
print(f"❌ {error_msg}")
|
|
196
|
+
print("💡 Try running with appropriate permissions or check directory ownership")
|
|
197
|
+
if self.project_logger:
|
|
198
|
+
self.project_logger.log_system(error_msg, level="ERROR", component="deployment")
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
except FileNotFoundError as e:
|
|
202
|
+
error_msg = f"Agent templates not found: {e}"
|
|
203
|
+
self.logger.error(error_msg)
|
|
204
|
+
print(f"❌ {error_msg}")
|
|
205
|
+
print("💡 Ensure claude-mpm is properly installed")
|
|
206
|
+
if self.project_logger:
|
|
207
|
+
self.project_logger.log_system(error_msg, level="ERROR", component="deployment")
|
|
208
|
+
return False
|
|
209
|
+
|
|
210
|
+
except ImportError as e:
|
|
211
|
+
error_msg = f"Missing required module for agent deployment: {e}"
|
|
212
|
+
self.logger.error(error_msg)
|
|
213
|
+
print(f"⚠️ {error_msg}")
|
|
214
|
+
print("💡 Some agent features may be limited")
|
|
215
|
+
if self.project_logger:
|
|
216
|
+
self.project_logger.log_system(error_msg, level="WARNING", component="deployment")
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
error_msg = f"Unexpected error during agent deployment: {e}"
|
|
221
|
+
self.logger.error(error_msg)
|
|
222
|
+
print(f"⚠️ {error_msg}")
|
|
223
|
+
if self.project_logger:
|
|
224
|
+
self.project_logger.log_system(error_msg, level="ERROR", component="deployment")
|
|
225
|
+
# Continue without agents rather than failing completely
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
def ensure_project_agents(self) -> bool:
|
|
229
|
+
"""Ensure system agents are available in the project directory.
|
|
230
|
+
|
|
231
|
+
Deploys system agents to project's .claude-mpm/agents/ directory
|
|
232
|
+
if they don't exist or are outdated. This enables project-level
|
|
233
|
+
agent customization and ensures all agents are available locally.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
bool: True if agents are available, False on error
|
|
237
|
+
"""
|
|
238
|
+
try:
|
|
239
|
+
# Check if we're in a project directory
|
|
240
|
+
project_dir = Path.cwd()
|
|
241
|
+
project_agents_dir = project_dir / ".claude-mpm" / "agents"
|
|
242
|
+
|
|
243
|
+
# Create directory if it doesn't exist
|
|
244
|
+
project_agents_dir.mkdir(parents=True, exist_ok=True)
|
|
245
|
+
|
|
246
|
+
if self.project_logger:
|
|
247
|
+
self.project_logger.log_system(
|
|
248
|
+
f"Ensuring agents are available in project: {project_agents_dir}",
|
|
249
|
+
level="INFO",
|
|
250
|
+
component="deployment"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Deploy agents to project directory with project deployment mode
|
|
254
|
+
# This ensures all system agents are deployed regardless of version
|
|
255
|
+
results = self.deployment_service.deploy_agents(
|
|
256
|
+
target_dir=project_dir / ".claude-mpm",
|
|
257
|
+
force_rebuild=False,
|
|
258
|
+
deployment_mode="project"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
if results["deployed"] or results.get("updated", []):
|
|
262
|
+
deployed_count = len(results['deployed'])
|
|
263
|
+
updated_count = len(results.get('updated', []))
|
|
264
|
+
|
|
265
|
+
if deployed_count > 0:
|
|
266
|
+
self.logger.info(f"Deployed {deployed_count} agents to project")
|
|
267
|
+
if updated_count > 0:
|
|
268
|
+
self.logger.info(f"Updated {updated_count} agents in project")
|
|
269
|
+
|
|
270
|
+
return True
|
|
271
|
+
elif results.get("skipped", []):
|
|
272
|
+
# Agents already exist and are current
|
|
273
|
+
self.logger.debug(f"Project agents up to date: {len(results['skipped'])} agents")
|
|
274
|
+
return True
|
|
275
|
+
else:
|
|
276
|
+
self.logger.warning("No agents deployed to project")
|
|
277
|
+
return False
|
|
278
|
+
|
|
151
279
|
except Exception as e:
|
|
152
|
-
self.logger.error(f"
|
|
153
|
-
print(f"⚠️ Agent deployment failed: {e}")
|
|
280
|
+
self.logger.error(f"Failed to ensure project agents: {e}")
|
|
154
281
|
if self.project_logger:
|
|
155
282
|
self.project_logger.log_system(
|
|
156
|
-
f"
|
|
283
|
+
f"Failed to ensure project agents: {e}",
|
|
157
284
|
level="ERROR",
|
|
158
285
|
component="deployment"
|
|
159
286
|
)
|
|
@@ -180,8 +307,14 @@ class ClaudeRunner:
|
|
|
180
307
|
launch_method=self.launch_method,
|
|
181
308
|
working_dir=working_dir
|
|
182
309
|
)
|
|
310
|
+
except ImportError as e:
|
|
311
|
+
self.logger.warning(f"Socket.IO module not available: {e}")
|
|
312
|
+
self.websocket_server = None
|
|
313
|
+
except ConnectionError as e:
|
|
314
|
+
self.logger.warning(f"Cannot connect to Socket.IO server on port {self.websocket_port}: {e}")
|
|
315
|
+
self.websocket_server = None
|
|
183
316
|
except Exception as e:
|
|
184
|
-
self.logger.warning(f"
|
|
317
|
+
self.logger.warning(f"Unexpected error with Socket.IO server: {e}")
|
|
185
318
|
self.websocket_server = None
|
|
186
319
|
|
|
187
320
|
# Get version with robust fallback mechanisms
|
|
@@ -246,8 +379,12 @@ class ClaudeRunner:
|
|
|
246
379
|
try:
|
|
247
380
|
os.chdir(user_pwd)
|
|
248
381
|
self.logger.info(f"Changed working directory to: {user_pwd}")
|
|
249
|
-
except
|
|
250
|
-
self.logger.warning(f"
|
|
382
|
+
except PermissionError as e:
|
|
383
|
+
self.logger.warning(f"Permission denied accessing directory {user_pwd}: {e}")
|
|
384
|
+
except FileNotFoundError as e:
|
|
385
|
+
self.logger.warning(f"Directory not found {user_pwd}: {e}")
|
|
386
|
+
except OSError as e:
|
|
387
|
+
self.logger.warning(f"OS error changing to directory {user_pwd}: {e}")
|
|
251
388
|
|
|
252
389
|
print("Launching Claude...")
|
|
253
390
|
|
|
@@ -283,18 +420,62 @@ class ClaudeRunner:
|
|
|
283
420
|
)
|
|
284
421
|
os.execvpe(cmd[0], cmd, clean_env)
|
|
285
422
|
|
|
286
|
-
except
|
|
287
|
-
|
|
423
|
+
except FileNotFoundError as e:
|
|
424
|
+
error_msg = f"Claude CLI not found. Please ensure 'claude' is installed and in your PATH: {e}"
|
|
425
|
+
print(f"❌ {error_msg}")
|
|
426
|
+
if self.project_logger:
|
|
427
|
+
self.project_logger.log_system(error_msg, level="ERROR", component="session")
|
|
428
|
+
self._log_session_event({
|
|
429
|
+
"event": "interactive_launch_failed",
|
|
430
|
+
"error": str(e),
|
|
431
|
+
"exception_type": "FileNotFoundError",
|
|
432
|
+
"recovery_action": "fallback_to_subprocess"
|
|
433
|
+
})
|
|
434
|
+
except PermissionError as e:
|
|
435
|
+
error_msg = f"Permission denied executing Claude CLI: {e}"
|
|
436
|
+
print(f"❌ {error_msg}")
|
|
437
|
+
if self.project_logger:
|
|
438
|
+
self.project_logger.log_system(error_msg, level="ERROR", component="session")
|
|
439
|
+
self._log_session_event({
|
|
440
|
+
"event": "interactive_launch_failed",
|
|
441
|
+
"error": str(e),
|
|
442
|
+
"exception_type": "PermissionError",
|
|
443
|
+
"recovery_action": "check_file_permissions"
|
|
444
|
+
})
|
|
445
|
+
except OSError as e:
|
|
446
|
+
error_msg = f"OS error launching Claude: {e}"
|
|
447
|
+
print(f"❌ {error_msg}")
|
|
448
|
+
if self.project_logger:
|
|
449
|
+
self.project_logger.log_system(error_msg, level="ERROR", component="session")
|
|
450
|
+
self._log_session_event({
|
|
451
|
+
"event": "interactive_launch_failed",
|
|
452
|
+
"error": str(e),
|
|
453
|
+
"exception_type": "OSError",
|
|
454
|
+
"recovery_action": "fallback_to_subprocess"
|
|
455
|
+
})
|
|
456
|
+
except KeyboardInterrupt:
|
|
457
|
+
print("\n⚠️ Session interrupted by user")
|
|
288
458
|
if self.project_logger:
|
|
289
459
|
self.project_logger.log_system(
|
|
290
|
-
|
|
291
|
-
level="
|
|
460
|
+
"Session interrupted by user",
|
|
461
|
+
level="INFO",
|
|
292
462
|
component="session"
|
|
293
463
|
)
|
|
464
|
+
self._log_session_event({
|
|
465
|
+
"event": "session_interrupted",
|
|
466
|
+
"reason": "user_interrupt"
|
|
467
|
+
})
|
|
468
|
+
return # Clean exit on user interrupt
|
|
469
|
+
except Exception as e:
|
|
470
|
+
error_msg = f"Unexpected error launching Claude: {e}"
|
|
471
|
+
print(f"❌ {error_msg}")
|
|
472
|
+
if self.project_logger:
|
|
473
|
+
self.project_logger.log_system(error_msg, level="ERROR", component="session")
|
|
294
474
|
self._log_session_event({
|
|
295
475
|
"event": "interactive_launch_failed",
|
|
296
476
|
"error": str(e),
|
|
297
|
-
"exception_type": type(e).__name__
|
|
477
|
+
"exception_type": type(e).__name__,
|
|
478
|
+
"recovery_action": "fallback_to_subprocess"
|
|
298
479
|
})
|
|
299
480
|
|
|
300
481
|
# Notify WebSocket clients of error
|
|
@@ -304,21 +485,52 @@ class ClaudeRunner:
|
|
|
304
485
|
message=f"Failed to launch Claude: {e}"
|
|
305
486
|
)
|
|
306
487
|
# Fallback to subprocess
|
|
488
|
+
print("\n🔄 Attempting fallback launch method...")
|
|
307
489
|
try:
|
|
308
490
|
# Use the same clean_env we prepared earlier
|
|
309
|
-
subprocess.run(cmd, stdin=None, stdout=None, stderr=None, env=clean_env)
|
|
491
|
+
result = subprocess.run(cmd, stdin=None, stdout=None, stderr=None, env=clean_env)
|
|
492
|
+
if result.returncode == 0:
|
|
493
|
+
if self.project_logger:
|
|
494
|
+
self.project_logger.log_system(
|
|
495
|
+
"Interactive session completed (subprocess fallback)",
|
|
496
|
+
level="INFO",
|
|
497
|
+
component="session"
|
|
498
|
+
)
|
|
499
|
+
self._log_session_event({
|
|
500
|
+
"event": "interactive_session_complete",
|
|
501
|
+
"fallback": True,
|
|
502
|
+
"return_code": result.returncode
|
|
503
|
+
})
|
|
504
|
+
else:
|
|
505
|
+
print(f"⚠️ Claude exited with code {result.returncode}")
|
|
506
|
+
if self.project_logger:
|
|
507
|
+
self.project_logger.log_system(
|
|
508
|
+
f"Claude exited with non-zero code: {result.returncode}",
|
|
509
|
+
level="WARNING",
|
|
510
|
+
component="session"
|
|
511
|
+
)
|
|
512
|
+
except FileNotFoundError as e:
|
|
513
|
+
print(f"❌ Fallback failed: Claude CLI not found in PATH")
|
|
514
|
+
print("\n💡 To fix this issue:")
|
|
515
|
+
print(" 1. Install Claude CLI: npm install -g @anthropic-ai/claude-ai")
|
|
516
|
+
print(" 2. Or specify the full path to the claude binary")
|
|
517
|
+
if self.project_logger:
|
|
518
|
+
self.project_logger.log_system(
|
|
519
|
+
f"Fallback failed - Claude CLI not found: {e}",
|
|
520
|
+
level="ERROR",
|
|
521
|
+
component="session"
|
|
522
|
+
)
|
|
523
|
+
except KeyboardInterrupt:
|
|
524
|
+
print("\n⚠️ Fallback interrupted by user")
|
|
310
525
|
if self.project_logger:
|
|
311
526
|
self.project_logger.log_system(
|
|
312
|
-
"
|
|
527
|
+
"Fallback interrupted by user",
|
|
313
528
|
level="INFO",
|
|
314
529
|
component="session"
|
|
315
530
|
)
|
|
316
|
-
self._log_session_event({
|
|
317
|
-
"event": "interactive_session_complete",
|
|
318
|
-
"fallback": True
|
|
319
|
-
})
|
|
320
531
|
except Exception as fallback_error:
|
|
321
|
-
print(f"Fallback
|
|
532
|
+
print(f"❌ Fallback failed with unexpected error: {fallback_error}")
|
|
533
|
+
print(f" Error type: {type(fallback_error).__name__}")
|
|
322
534
|
if self.project_logger:
|
|
323
535
|
self.project_logger.log_system(
|
|
324
536
|
f"Fallback launch failed: {fallback_error}",
|
|
@@ -354,8 +566,14 @@ class ClaudeRunner:
|
|
|
354
566
|
launch_method="oneshot",
|
|
355
567
|
working_dir=working_dir
|
|
356
568
|
)
|
|
569
|
+
except ImportError as e:
|
|
570
|
+
self.logger.warning(f"Socket.IO module not available: {e}")
|
|
571
|
+
self.websocket_server = None
|
|
572
|
+
except ConnectionError as e:
|
|
573
|
+
self.logger.warning(f"Cannot connect to Socket.IO server on port {self.websocket_port}: {e}")
|
|
574
|
+
self.websocket_server = None
|
|
357
575
|
except Exception as e:
|
|
358
|
-
self.logger.warning(f"
|
|
576
|
+
self.logger.warning(f"Unexpected error with Socket.IO server: {e}")
|
|
359
577
|
self.websocket_server = None
|
|
360
578
|
|
|
361
579
|
# Check for /mpm: commands
|
|
@@ -412,8 +630,14 @@ class ClaudeRunner:
|
|
|
412
630
|
original_cwd = os.getcwd()
|
|
413
631
|
os.chdir(user_pwd)
|
|
414
632
|
self.logger.info(f"Changed working directory to: {user_pwd}")
|
|
415
|
-
except
|
|
416
|
-
self.logger.warning(f"
|
|
633
|
+
except PermissionError as e:
|
|
634
|
+
self.logger.warning(f"Permission denied accessing directory {user_pwd}: {e}")
|
|
635
|
+
original_cwd = None
|
|
636
|
+
except FileNotFoundError as e:
|
|
637
|
+
self.logger.warning(f"Directory not found {user_pwd}: {e}")
|
|
638
|
+
original_cwd = None
|
|
639
|
+
except OSError as e:
|
|
640
|
+
self.logger.warning(f"OS error changing to directory {user_pwd}: {e}")
|
|
417
641
|
original_cwd = None
|
|
418
642
|
else:
|
|
419
643
|
original_cwd = None
|
|
@@ -523,9 +747,79 @@ class ClaudeRunner:
|
|
|
523
747
|
})
|
|
524
748
|
|
|
525
749
|
return False
|
|
526
|
-
|
|
750
|
+
|
|
751
|
+
except subprocess.TimeoutExpired as e:
|
|
752
|
+
error_msg = f"Command timed out after {e.timeout} seconds"
|
|
753
|
+
print(f"⏱️ {error_msg}")
|
|
754
|
+
if self.project_logger:
|
|
755
|
+
self.project_logger.log_system(error_msg, level="ERROR", component="session")
|
|
756
|
+
self._log_session_event({
|
|
757
|
+
"event": "session_timeout",
|
|
758
|
+
"success": False,
|
|
759
|
+
"timeout": e.timeout,
|
|
760
|
+
"exception_type": "TimeoutExpired"
|
|
761
|
+
})
|
|
762
|
+
return False
|
|
763
|
+
|
|
764
|
+
except FileNotFoundError as e:
|
|
765
|
+
error_msg = "Claude CLI not found. Please ensure 'claude' is installed and in your PATH"
|
|
766
|
+
print(f"❌ {error_msg}")
|
|
767
|
+
print("\n💡 To fix: Install Claude CLI with 'npm install -g @anthropic-ai/claude-ai'")
|
|
768
|
+
if self.project_logger:
|
|
769
|
+
self.project_logger.log_system(f"{error_msg}: {e}", level="ERROR", component="session")
|
|
770
|
+
self._log_session_event({
|
|
771
|
+
"event": "session_exception",
|
|
772
|
+
"success": False,
|
|
773
|
+
"exception": str(e),
|
|
774
|
+
"exception_type": "FileNotFoundError"
|
|
775
|
+
})
|
|
776
|
+
return False
|
|
777
|
+
|
|
778
|
+
except PermissionError as e:
|
|
779
|
+
error_msg = f"Permission denied executing Claude CLI: {e}"
|
|
780
|
+
print(f"❌ {error_msg}")
|
|
781
|
+
if self.project_logger:
|
|
782
|
+
self.project_logger.log_system(error_msg, level="ERROR", component="session")
|
|
783
|
+
self._log_session_event({
|
|
784
|
+
"event": "session_exception",
|
|
785
|
+
"success": False,
|
|
786
|
+
"exception": str(e),
|
|
787
|
+
"exception_type": "PermissionError"
|
|
788
|
+
})
|
|
789
|
+
return False
|
|
790
|
+
|
|
791
|
+
except KeyboardInterrupt:
|
|
792
|
+
print("\n⚠️ Command interrupted by user")
|
|
793
|
+
if self.project_logger:
|
|
794
|
+
self.project_logger.log_system(
|
|
795
|
+
"Session interrupted by user",
|
|
796
|
+
level="INFO",
|
|
797
|
+
component="session"
|
|
798
|
+
)
|
|
799
|
+
self._log_session_event({
|
|
800
|
+
"event": "session_interrupted",
|
|
801
|
+
"success": False,
|
|
802
|
+
"reason": "user_interrupt"
|
|
803
|
+
})
|
|
804
|
+
return False
|
|
805
|
+
|
|
806
|
+
except MemoryError as e:
|
|
807
|
+
error_msg = "Out of memory while processing command"
|
|
808
|
+
print(f"❌ {error_msg}")
|
|
809
|
+
if self.project_logger:
|
|
810
|
+
self.project_logger.log_system(f"{error_msg}: {e}", level="ERROR", component="session")
|
|
811
|
+
self._log_session_event({
|
|
812
|
+
"event": "session_exception",
|
|
813
|
+
"success": False,
|
|
814
|
+
"exception": str(e),
|
|
815
|
+
"exception_type": "MemoryError"
|
|
816
|
+
})
|
|
817
|
+
return False
|
|
818
|
+
|
|
527
819
|
except Exception as e:
|
|
528
|
-
|
|
820
|
+
error_msg = f"Unexpected error: {e}"
|
|
821
|
+
print(f"❌ {error_msg}")
|
|
822
|
+
print(f" Error type: {type(e).__name__}")
|
|
529
823
|
|
|
530
824
|
if self.project_logger:
|
|
531
825
|
self.project_logger.log_system(
|
|
@@ -580,8 +874,12 @@ class ClaudeRunner:
|
|
|
580
874
|
print(f" ... and {len(tickets) - 3} more")
|
|
581
875
|
else:
|
|
582
876
|
self.logger.debug("Ticket extraction method not available")
|
|
877
|
+
except AttributeError as e:
|
|
878
|
+
self.logger.debug(f"Ticket manager missing expected method: {e}")
|
|
879
|
+
except TypeError as e:
|
|
880
|
+
self.logger.debug(f"Invalid ticket data format: {e}")
|
|
583
881
|
except Exception as e:
|
|
584
|
-
self.logger.debug(f"
|
|
882
|
+
self.logger.debug(f"Unexpected error during ticket extraction: {e}")
|
|
585
883
|
|
|
586
884
|
def _load_system_instructions(self) -> Optional[str]:
|
|
587
885
|
"""Load and process system instructions from agents/INSTRUCTIONS.md.
|
|
@@ -714,6 +1012,9 @@ class ClaudeRunner:
|
|
|
714
1012
|
component="command"
|
|
715
1013
|
)
|
|
716
1014
|
return True
|
|
1015
|
+
except ImportError as e:
|
|
1016
|
+
print(f"Error: CLI module not available: {e}")
|
|
1017
|
+
return False
|
|
717
1018
|
except Exception as e:
|
|
718
1019
|
print(f"Error getting agent versions: {e}")
|
|
719
1020
|
return False
|
|
@@ -722,6 +1023,9 @@ class ClaudeRunner:
|
|
|
722
1023
|
print("Available commands: test, agents")
|
|
723
1024
|
return True
|
|
724
1025
|
|
|
1026
|
+
except KeyboardInterrupt:
|
|
1027
|
+
print("\nCommand interrupted")
|
|
1028
|
+
return False
|
|
725
1029
|
except Exception as e:
|
|
726
1030
|
print(f"Error executing command: {e}")
|
|
727
1031
|
if self.project_logger:
|
|
@@ -743,6 +1047,8 @@ class ClaudeRunner:
|
|
|
743
1047
|
|
|
744
1048
|
with open(self.session_log_file, 'a') as f:
|
|
745
1049
|
f.write(json.dumps(log_entry) + '\n')
|
|
1050
|
+
except (OSError, IOError) as e:
|
|
1051
|
+
self.logger.debug(f"IO error logging session event: {e}")
|
|
746
1052
|
except Exception as e:
|
|
747
1053
|
self.logger.debug(f"Failed to log session event: {e}")
|
|
748
1054
|
|
|
@@ -793,14 +1099,13 @@ class ClaudeRunner:
|
|
|
793
1099
|
# Method 3: Try reading VERSION file directly (development fallback)
|
|
794
1100
|
if version == "0.0.0":
|
|
795
1101
|
try:
|
|
796
|
-
#
|
|
797
|
-
version_file
|
|
798
|
-
|
|
799
|
-
version = version_file.read_text().strip()
|
|
1102
|
+
# Use centralized path management for VERSION file
|
|
1103
|
+
if paths.version_file.exists():
|
|
1104
|
+
version = paths.version_file.read_text().strip()
|
|
800
1105
|
method_used = "version_file"
|
|
801
1106
|
self.logger.debug(f"Version obtained via VERSION file: {version}")
|
|
802
1107
|
else:
|
|
803
|
-
self.logger.debug(f"VERSION file not found at: {version_file}")
|
|
1108
|
+
self.logger.debug(f"VERSION file not found at: {paths.version_file}")
|
|
804
1109
|
except Exception as e:
|
|
805
1110
|
self.logger.warning(f"Failed to read VERSION file: {e}")
|
|
806
1111
|
|
|
@@ -832,10 +1137,14 @@ class ClaudeRunner:
|
|
|
832
1137
|
return
|
|
833
1138
|
|
|
834
1139
|
# Import hook classes (lazy import to avoid circular dependencies)
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1140
|
+
try:
|
|
1141
|
+
from claude_mpm.hooks.memory_integration_hook import (
|
|
1142
|
+
MemoryPreDelegationHook,
|
|
1143
|
+
MemoryPostDelegationHook
|
|
1144
|
+
)
|
|
1145
|
+
except ImportError as e:
|
|
1146
|
+
self.logger.warning(f"Memory integration hooks not available: {e}")
|
|
1147
|
+
return
|
|
839
1148
|
|
|
840
1149
|
# Register pre-delegation hook for memory injection
|
|
841
1150
|
pre_hook = MemoryPreDelegationHook(self.config)
|
|
@@ -862,6 +1171,8 @@ class ClaudeRunner:
|
|
|
862
1171
|
post_count = len(hooks.get('post_delegation', []))
|
|
863
1172
|
self.logger.info(f"📋 Hook Service initialized: {pre_count} pre-delegation, {post_count} post-delegation hooks")
|
|
864
1173
|
|
|
1174
|
+
except AttributeError as e:
|
|
1175
|
+
self.logger.warning(f"Hook service not initialized properly: {e}")
|
|
865
1176
|
except Exception as e:
|
|
866
1177
|
self.logger.error(f"❌ Failed to register memory hooks: {e}")
|
|
867
1178
|
# Don't fail the entire initialization - memory system is optional
|
claude_mpm/core/config.py
CHANGED
|
@@ -11,6 +11,7 @@ from typing import Any, Dict, Optional, Union
|
|
|
11
11
|
import logging
|
|
12
12
|
|
|
13
13
|
from ..utils.config_manager import ConfigurationManager
|
|
14
|
+
from .config_paths import ConfigPaths
|
|
14
15
|
|
|
15
16
|
logger = logging.getLogger(__name__)
|
|
16
17
|
|
|
@@ -51,6 +52,11 @@ class Config:
|
|
|
51
52
|
# Load from file if provided
|
|
52
53
|
if config_file:
|
|
53
54
|
self.load_file(config_file)
|
|
55
|
+
else:
|
|
56
|
+
# Try to load from standard location: .claude-mpm/configuration.yaml
|
|
57
|
+
default_config = Path.cwd() / ".claude-mpm" / "configuration.yaml"
|
|
58
|
+
if default_config.exists():
|
|
59
|
+
self.load_file(default_config)
|
|
54
60
|
|
|
55
61
|
# Load from environment variables (new and legacy prefixes)
|
|
56
62
|
self._load_env_vars()
|
|
@@ -220,7 +226,7 @@ class Config:
|
|
|
220
226
|
"fallback_tracking_method": "logging", # Options: "logging", "file", "disabled"
|
|
221
227
|
# Evaluation system - Phase 2 Mirascope integration
|
|
222
228
|
"enable_evaluation": True,
|
|
223
|
-
"evaluation_storage_path": str(
|
|
229
|
+
"evaluation_storage_path": str(ConfigPaths.get_user_config_dir() / "training"),
|
|
224
230
|
"correction_capture_enabled": True,
|
|
225
231
|
"correction_storage_rotation_days": 30,
|
|
226
232
|
"evaluation_logging_enabled": True,
|
|
@@ -5,10 +5,10 @@ Manages friendly directory aliases for configuration paths, allowing users to
|
|
|
5
5
|
reference configurations using memorable names instead of full paths.
|
|
6
6
|
|
|
7
7
|
Example:
|
|
8
|
-
claude-pm --config personal # Resolves to ~/.claude-
|
|
8
|
+
claude-pm --config personal # Resolves to ~/.claude-mpm/configs/personal/
|
|
9
9
|
claude-pm --config work # Resolves to ~/work/claude-configs/
|
|
10
10
|
|
|
11
|
-
Aliases are stored in ~/.claude-
|
|
11
|
+
Aliases are stored in ~/.claude-mpm/config_aliases.json
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
import json
|
|
@@ -18,6 +18,7 @@ from pathlib import Path
|
|
|
18
18
|
from typing import Dict, Optional, List, Tuple
|
|
19
19
|
|
|
20
20
|
from ..utils.config_manager import ConfigurationManager
|
|
21
|
+
from .config_paths import ConfigPaths
|
|
21
22
|
|
|
22
23
|
logger = logging.getLogger(__name__)
|
|
23
24
|
|
|
@@ -59,7 +60,7 @@ class ConfigAliasManager:
|
|
|
59
60
|
~/.claude-pm/config_aliases.json
|
|
60
61
|
"""
|
|
61
62
|
if aliases_file is None:
|
|
62
|
-
self.aliases_file =
|
|
63
|
+
self.aliases_file = ConfigPaths.get_user_config_dir() / "config_aliases.json"
|
|
63
64
|
else:
|
|
64
65
|
self.aliases_file = Path(aliases_file)
|
|
65
66
|
|