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/init.py
CHANGED
|
@@ -71,18 +71,23 @@ class ProjectInitializer:
|
|
|
71
71
|
- agents/
|
|
72
72
|
- project-specific/
|
|
73
73
|
- config/
|
|
74
|
+
- responses/
|
|
74
75
|
- logs/
|
|
75
76
|
"""
|
|
76
77
|
try:
|
|
77
|
-
# Find project root
|
|
78
|
+
# Find project root - always define project_root for consistent messaging
|
|
78
79
|
if project_path:
|
|
80
|
+
project_root = project_path
|
|
79
81
|
self.project_dir = project_path / ".claude-mpm"
|
|
80
82
|
else:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
# Always use current working directory for project directories
|
|
84
|
+
# This ensures .claude-mpm is created where the user launches the tool
|
|
85
|
+
project_root = Path.cwd()
|
|
84
86
|
self.project_dir = project_root / ".claude-mpm"
|
|
85
87
|
|
|
88
|
+
# Check if directory already exists
|
|
89
|
+
directory_existed = self.project_dir.exists()
|
|
90
|
+
|
|
86
91
|
# Create project directory
|
|
87
92
|
self.project_dir.mkdir(exist_ok=True)
|
|
88
93
|
|
|
@@ -90,6 +95,7 @@ class ProjectInitializer:
|
|
|
90
95
|
directories = [
|
|
91
96
|
self.project_dir / "agents" / "project-specific",
|
|
92
97
|
self.project_dir / "config",
|
|
98
|
+
self.project_dir / "responses",
|
|
93
99
|
self.project_dir / "logs",
|
|
94
100
|
]
|
|
95
101
|
|
|
@@ -106,11 +112,21 @@ class ProjectInitializer:
|
|
|
106
112
|
if not gitignore.exists():
|
|
107
113
|
gitignore.write_text("logs/\n*.log\n*.pyc\n__pycache__/\n")
|
|
108
114
|
|
|
115
|
+
# Log successful creation with details
|
|
109
116
|
self.logger.info(f"Initialized project directory at {self.project_dir}")
|
|
117
|
+
self.logger.debug(f"Created directories: agents, config, responses, logs")
|
|
118
|
+
|
|
119
|
+
# Print appropriate message to console for visibility during startup
|
|
120
|
+
if directory_existed:
|
|
121
|
+
print(f"✓ Found existing .claude-mpm/ directory in {project_root}")
|
|
122
|
+
else:
|
|
123
|
+
print(f"✓ Initialized .claude-mpm/ in {project_root}")
|
|
124
|
+
|
|
110
125
|
return True
|
|
111
126
|
|
|
112
127
|
except Exception as e:
|
|
113
128
|
self.logger.error(f"Failed to initialize project directory: {e}")
|
|
129
|
+
print(f"✗ Failed to create .claude-mpm/ directory: {e}")
|
|
114
130
|
return False
|
|
115
131
|
|
|
116
132
|
def _find_project_root(self) -> Optional[Path]:
|
|
@@ -198,7 +214,6 @@ class ProjectInitializer:
|
|
|
198
214
|
'ai_trackdown_pytools',
|
|
199
215
|
'yaml',
|
|
200
216
|
'dotenv',
|
|
201
|
-
'rich',
|
|
202
217
|
'click',
|
|
203
218
|
'pexpect',
|
|
204
219
|
'psutil',
|
|
@@ -218,9 +233,25 @@ class ProjectInitializer:
|
|
|
218
233
|
return dependencies
|
|
219
234
|
|
|
220
235
|
def ensure_initialized(self) -> bool:
|
|
221
|
-
"""Ensure both user and project directories are initialized.
|
|
236
|
+
"""Ensure both user and project directories are initialized.
|
|
237
|
+
|
|
238
|
+
Shows clear information about where directories are being created.
|
|
239
|
+
"""
|
|
240
|
+
# Show working directory info at startup
|
|
241
|
+
cwd = Path.cwd()
|
|
242
|
+
framework_path = Path(__file__).parent.parent.parent
|
|
243
|
+
|
|
244
|
+
# Log startup context
|
|
245
|
+
self.logger.info(f"Working directory: {cwd}")
|
|
246
|
+
self.logger.info(f"Framework path: {framework_path}")
|
|
247
|
+
|
|
248
|
+
# Initialize user directory (in home)
|
|
222
249
|
user_ok = self.initialize_user_directory()
|
|
250
|
+
|
|
251
|
+
# Initialize project directory (in current working directory)
|
|
252
|
+
self.logger.info(f"Checking for .claude-mpm/ in {cwd}")
|
|
223
253
|
project_ok = self.initialize_project_directory()
|
|
254
|
+
|
|
224
255
|
return user_ok and project_ok
|
|
225
256
|
|
|
226
257
|
|
|
@@ -71,8 +71,12 @@ except ImportError:
|
|
|
71
71
|
print(" 3. Check that PYTHONPATH includes the package location")
|
|
72
72
|
sys.exit(1)
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
# Use deployment root for daemon files to keep everything centralized
|
|
75
|
+
from claude_mpm.deployment_paths import get_project_root
|
|
76
|
+
|
|
77
|
+
deployment_root = get_project_root()
|
|
78
|
+
PID_FILE = deployment_root / ".claude-mpm" / "socketio-server.pid"
|
|
79
|
+
LOG_FILE = deployment_root / ".claude-mpm" / "socketio-server.log"
|
|
76
80
|
|
|
77
81
|
def ensure_dirs():
|
|
78
82
|
"""Ensure required directories exist."""
|
claude_mpm/services/__init__.py
CHANGED
|
@@ -7,14 +7,48 @@ def __getattr__(name):
|
|
|
7
7
|
from .ticket_manager import TicketManager
|
|
8
8
|
return TicketManager
|
|
9
9
|
elif name == "AgentDeploymentService":
|
|
10
|
-
from .
|
|
10
|
+
from .agents.deployment import AgentDeploymentService
|
|
11
11
|
return AgentDeploymentService
|
|
12
12
|
elif name == "AgentMemoryManager":
|
|
13
|
-
from .
|
|
13
|
+
from .agents.memory import AgentMemoryManager
|
|
14
14
|
return AgentMemoryManager
|
|
15
15
|
elif name == "get_memory_manager":
|
|
16
|
-
from .
|
|
16
|
+
from .agents.memory import get_memory_manager
|
|
17
17
|
return get_memory_manager
|
|
18
|
+
# Add backward compatibility for other agent services
|
|
19
|
+
elif name == "AgentRegistry":
|
|
20
|
+
from .agents.registry import AgentRegistry
|
|
21
|
+
return AgentRegistry
|
|
22
|
+
elif name == "AgentLifecycleManager":
|
|
23
|
+
from .agents.deployment import AgentLifecycleManager
|
|
24
|
+
return AgentLifecycleManager
|
|
25
|
+
elif name == "AgentManager":
|
|
26
|
+
from .agents.management import AgentManager
|
|
27
|
+
return AgentManager
|
|
28
|
+
elif name == "AgentCapabilitiesGenerator":
|
|
29
|
+
from .agents.management import AgentCapabilitiesGenerator
|
|
30
|
+
return AgentCapabilitiesGenerator
|
|
31
|
+
elif name == "AgentModificationTracker":
|
|
32
|
+
from .agents.registry import AgentModificationTracker
|
|
33
|
+
return AgentModificationTracker
|
|
34
|
+
elif name == "AgentPersistenceService":
|
|
35
|
+
from .agents.memory import AgentPersistenceService
|
|
36
|
+
return AgentPersistenceService
|
|
37
|
+
elif name == "AgentProfileLoader":
|
|
38
|
+
from .agents.loading import AgentProfileLoader
|
|
39
|
+
return AgentProfileLoader
|
|
40
|
+
elif name == "AgentVersionManager":
|
|
41
|
+
from .agents.deployment import AgentVersionManager
|
|
42
|
+
return AgentVersionManager
|
|
43
|
+
elif name == "BaseAgentManager":
|
|
44
|
+
from .agents.loading import BaseAgentManager
|
|
45
|
+
return BaseAgentManager
|
|
46
|
+
elif name == "DeployedAgentDiscovery":
|
|
47
|
+
from .agents.registry import DeployedAgentDiscovery
|
|
48
|
+
return DeployedAgentDiscovery
|
|
49
|
+
elif name == "FrameworkAgentLoader":
|
|
50
|
+
from .agents.loading import FrameworkAgentLoader
|
|
51
|
+
return FrameworkAgentLoader
|
|
18
52
|
elif name == "HookService":
|
|
19
53
|
from .hook_service import HookService
|
|
20
54
|
return HookService
|
|
@@ -36,6 +70,22 @@ def __getattr__(name):
|
|
|
36
70
|
elif name == "StandaloneSocketIOServer":
|
|
37
71
|
from .standalone_socketio_server import StandaloneSocketIOServer
|
|
38
72
|
return StandaloneSocketIOServer
|
|
73
|
+
# Backward compatibility for memory services
|
|
74
|
+
elif name == "MemoryBuilder":
|
|
75
|
+
from .memory.builder import MemoryBuilder
|
|
76
|
+
return MemoryBuilder
|
|
77
|
+
elif name == "MemoryRouter":
|
|
78
|
+
from .memory.router import MemoryRouter
|
|
79
|
+
return MemoryRouter
|
|
80
|
+
elif name == "MemoryOptimizer":
|
|
81
|
+
from .memory.optimizer import MemoryOptimizer
|
|
82
|
+
return MemoryOptimizer
|
|
83
|
+
elif name == "SimpleCacheService":
|
|
84
|
+
from .memory.cache.simple_cache import SimpleCacheService
|
|
85
|
+
return SimpleCacheService
|
|
86
|
+
elif name == "SharedPromptCache":
|
|
87
|
+
from .memory.cache.shared_prompt_cache import SharedPromptCache
|
|
88
|
+
return SharedPromptCache
|
|
39
89
|
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
40
90
|
|
|
41
91
|
__all__ = [
|
|
@@ -48,4 +98,22 @@ __all__ = [
|
|
|
48
98
|
"AdvancedHealthMonitor",
|
|
49
99
|
"RecoveryManager",
|
|
50
100
|
"StandaloneSocketIOServer",
|
|
101
|
+
# Additional agent services for backward compatibility
|
|
102
|
+
"AgentRegistry",
|
|
103
|
+
"AgentLifecycleManager",
|
|
104
|
+
"AgentManager",
|
|
105
|
+
"AgentCapabilitiesGenerator",
|
|
106
|
+
"AgentModificationTracker",
|
|
107
|
+
"AgentPersistenceService",
|
|
108
|
+
"AgentProfileLoader",
|
|
109
|
+
"AgentVersionManager",
|
|
110
|
+
"BaseAgentManager",
|
|
111
|
+
"DeployedAgentDiscovery",
|
|
112
|
+
"FrameworkAgentLoader",
|
|
113
|
+
# Memory services (backward compatibility)
|
|
114
|
+
"MemoryBuilder",
|
|
115
|
+
"MemoryRouter",
|
|
116
|
+
"MemoryOptimizer",
|
|
117
|
+
"SimpleCacheService",
|
|
118
|
+
"SharedPromptCache",
|
|
51
119
|
]
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Agent services module - hierarchical organization of agent-related services."""
|
|
2
|
+
|
|
3
|
+
# Registry exports
|
|
4
|
+
from .registry.agent_registry import (
|
|
5
|
+
AgentRegistry,
|
|
6
|
+
AgentMetadata,
|
|
7
|
+
AgentTier,
|
|
8
|
+
AgentType,
|
|
9
|
+
)
|
|
10
|
+
from .registry.deployed_agent_discovery import DeployedAgentDiscovery
|
|
11
|
+
from .registry.modification_tracker import (
|
|
12
|
+
AgentModificationTracker,
|
|
13
|
+
ModificationType,
|
|
14
|
+
ModificationTier,
|
|
15
|
+
AgentModification,
|
|
16
|
+
ModificationHistory,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# Loading exports
|
|
20
|
+
from .loading.framework_agent_loader import FrameworkAgentLoader
|
|
21
|
+
from .loading.agent_profile_loader import AgentProfileLoader
|
|
22
|
+
from .loading.base_agent_manager import BaseAgentManager
|
|
23
|
+
|
|
24
|
+
# Deployment exports
|
|
25
|
+
from .deployment.agent_deployment import AgentDeploymentService
|
|
26
|
+
from .deployment.agent_lifecycle_manager import (
|
|
27
|
+
AgentLifecycleManager,
|
|
28
|
+
LifecycleState,
|
|
29
|
+
LifecycleOperation,
|
|
30
|
+
AgentLifecycleRecord,
|
|
31
|
+
LifecycleOperationResult,
|
|
32
|
+
)
|
|
33
|
+
from .deployment.agent_versioning import AgentVersionManager
|
|
34
|
+
|
|
35
|
+
# Memory exports
|
|
36
|
+
from .memory.agent_memory_manager import (
|
|
37
|
+
AgentMemoryManager,
|
|
38
|
+
get_memory_manager,
|
|
39
|
+
)
|
|
40
|
+
from .memory.agent_persistence_service import (
|
|
41
|
+
AgentPersistenceService,
|
|
42
|
+
PersistenceStrategy,
|
|
43
|
+
PersistenceOperation,
|
|
44
|
+
PersistenceRecord,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Management exports
|
|
48
|
+
from .management.agent_management_service import AgentManager
|
|
49
|
+
from .management.agent_capabilities_generator import AgentCapabilitiesGenerator
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
# Registry
|
|
53
|
+
"AgentRegistry",
|
|
54
|
+
"AgentMetadata",
|
|
55
|
+
"AgentTier",
|
|
56
|
+
"AgentType",
|
|
57
|
+
"DeployedAgentDiscovery",
|
|
58
|
+
"AgentModificationTracker",
|
|
59
|
+
"ModificationType",
|
|
60
|
+
"ModificationTier",
|
|
61
|
+
"AgentModification",
|
|
62
|
+
"ModificationHistory",
|
|
63
|
+
# Loading
|
|
64
|
+
"FrameworkAgentLoader",
|
|
65
|
+
"AgentProfileLoader",
|
|
66
|
+
"BaseAgentManager",
|
|
67
|
+
# Deployment
|
|
68
|
+
"AgentDeploymentService",
|
|
69
|
+
"AgentLifecycleManager",
|
|
70
|
+
"LifecycleState",
|
|
71
|
+
"LifecycleOperation",
|
|
72
|
+
"AgentLifecycleRecord",
|
|
73
|
+
"LifecycleOperationResult",
|
|
74
|
+
"AgentVersionManager",
|
|
75
|
+
# Memory
|
|
76
|
+
"AgentMemoryManager",
|
|
77
|
+
"get_memory_manager",
|
|
78
|
+
"AgentPersistenceService",
|
|
79
|
+
"PersistenceStrategy",
|
|
80
|
+
"PersistenceOperation",
|
|
81
|
+
"PersistenceRecord",
|
|
82
|
+
# Management
|
|
83
|
+
"AgentManager",
|
|
84
|
+
"AgentCapabilitiesGenerator",
|
|
85
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Agent deployment and lifecycle management services."""
|
|
2
|
+
|
|
3
|
+
from .agent_deployment import AgentDeploymentService
|
|
4
|
+
from .agent_lifecycle_manager import (
|
|
5
|
+
AgentLifecycleManager,
|
|
6
|
+
LifecycleState,
|
|
7
|
+
LifecycleOperation,
|
|
8
|
+
AgentLifecycleRecord,
|
|
9
|
+
LifecycleOperationResult,
|
|
10
|
+
)
|
|
11
|
+
from .agent_versioning import AgentVersionManager
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"AgentDeploymentService",
|
|
15
|
+
"AgentLifecycleManager",
|
|
16
|
+
"LifecycleState",
|
|
17
|
+
"LifecycleOperation",
|
|
18
|
+
"AgentLifecycleRecord",
|
|
19
|
+
"LifecycleOperationResult",
|
|
20
|
+
"AgentVersionManager",
|
|
21
|
+
]
|
|
@@ -36,6 +36,7 @@ from typing import Optional, List, Dict, Any
|
|
|
36
36
|
|
|
37
37
|
from claude_mpm.core.logger import get_logger
|
|
38
38
|
from claude_mpm.constants import EnvironmentVars, Paths, AgentMetadata
|
|
39
|
+
from claude_mpm.config.paths import paths
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
class AgentDeploymentService:
|
|
@@ -99,29 +100,32 @@ class AgentDeploymentService:
|
|
|
99
100
|
'deployment_errors': {} # Track error types and frequencies
|
|
100
101
|
}
|
|
101
102
|
|
|
102
|
-
# Find templates directory
|
|
103
|
-
module_path = Path(__file__).parent.parent
|
|
103
|
+
# Find templates directory using centralized path management
|
|
104
104
|
if templates_dir:
|
|
105
105
|
self.templates_dir = Path(templates_dir)
|
|
106
106
|
else:
|
|
107
|
-
#
|
|
108
|
-
self.templates_dir =
|
|
107
|
+
# Use centralized paths instead of fragile parent calculations
|
|
108
|
+
self.templates_dir = paths.agents_dir / "templates"
|
|
109
109
|
|
|
110
110
|
# Find base agent file
|
|
111
111
|
if base_agent_path:
|
|
112
112
|
self.base_agent_path = Path(base_agent_path)
|
|
113
113
|
else:
|
|
114
|
-
#
|
|
115
|
-
self.base_agent_path =
|
|
114
|
+
# Use centralized paths for consistency
|
|
115
|
+
self.base_agent_path = paths.agents_dir / "base_agent.json"
|
|
116
116
|
|
|
117
117
|
self.logger.info(f"Templates directory: {self.templates_dir}")
|
|
118
118
|
self.logger.info(f"Base agent path: {self.base_agent_path}")
|
|
119
119
|
|
|
120
|
-
def deploy_agents(self, target_dir: Optional[Path] = None, force_rebuild: bool = False) -> Dict[str, Any]:
|
|
120
|
+
def deploy_agents(self, target_dir: Optional[Path] = None, force_rebuild: bool = False, deployment_mode: str = "update") -> Dict[str, Any]:
|
|
121
121
|
"""
|
|
122
122
|
Build and deploy agents by combining base_agent.md with templates.
|
|
123
123
|
Also deploys system instructions for PM framework.
|
|
124
124
|
|
|
125
|
+
DEPLOYMENT MODES:
|
|
126
|
+
- "update": Normal update mode - skip agents with matching versions (default)
|
|
127
|
+
- "project": Project deployment mode - always deploy all agents regardless of version
|
|
128
|
+
|
|
125
129
|
METRICS COLLECTED:
|
|
126
130
|
- Deployment start/end timestamps
|
|
127
131
|
- Individual agent deployment durations
|
|
@@ -161,6 +165,7 @@ class AgentDeploymentService:
|
|
|
161
165
|
Args:
|
|
162
166
|
target_dir: Target directory for agents (default: .claude/agents/)
|
|
163
167
|
force_rebuild: Force rebuild even if agents exist (useful for troubleshooting)
|
|
168
|
+
deployment_mode: "update" for version-aware updates, "project" for always deploy
|
|
164
169
|
|
|
165
170
|
Returns:
|
|
166
171
|
Dictionary with deployment results:
|
|
@@ -176,11 +181,28 @@ class AgentDeploymentService:
|
|
|
176
181
|
deployment_start_time = time.time()
|
|
177
182
|
|
|
178
183
|
if not target_dir:
|
|
179
|
-
|
|
184
|
+
# Default to user's home .claude/agents directory
|
|
185
|
+
agents_dir = Path(Paths.CLAUDE_AGENTS_DIR.value).expanduser()
|
|
186
|
+
else:
|
|
187
|
+
# If target_dir provided, use it directly (caller decides structure)
|
|
188
|
+
# This allows for both passing a project dir or the full agents path
|
|
189
|
+
target_dir = Path(target_dir)
|
|
190
|
+
# Check if this is already an agents directory
|
|
191
|
+
if target_dir.name == "agents":
|
|
192
|
+
# Already an agents directory, use as-is
|
|
193
|
+
agents_dir = target_dir
|
|
194
|
+
elif target_dir.name == ".claude-mpm":
|
|
195
|
+
# .claude-mpm directory, add agents subdirectory
|
|
196
|
+
agents_dir = target_dir / "agents"
|
|
197
|
+
elif target_dir.name == ".claude":
|
|
198
|
+
# .claude directory, add agents subdirectory
|
|
199
|
+
agents_dir = target_dir / "agents"
|
|
200
|
+
else:
|
|
201
|
+
# Assume it's a project directory, add .claude/agents
|
|
202
|
+
agents_dir = target_dir / ".claude" / "agents"
|
|
180
203
|
|
|
181
|
-
target_dir = Path(target_dir)
|
|
182
204
|
results = {
|
|
183
|
-
"target_dir": str(
|
|
205
|
+
"target_dir": str(agents_dir),
|
|
184
206
|
"deployed": [],
|
|
185
207
|
"errors": [],
|
|
186
208
|
"skipped": [],
|
|
@@ -200,9 +222,9 @@ class AgentDeploymentService:
|
|
|
200
222
|
}
|
|
201
223
|
|
|
202
224
|
try:
|
|
203
|
-
# Create
|
|
204
|
-
|
|
205
|
-
self.logger.info(f"Building and deploying agents to: {
|
|
225
|
+
# Create agents directory if needed
|
|
226
|
+
agents_dir.mkdir(parents=True, exist_ok=True)
|
|
227
|
+
self.logger.info(f"Building and deploying agents to: {agents_dir}")
|
|
206
228
|
|
|
207
229
|
# Note: System instructions are now loaded directly by SimpleClaudeRunner
|
|
208
230
|
|
|
@@ -214,7 +236,7 @@ class AgentDeploymentService:
|
|
|
214
236
|
return results
|
|
215
237
|
|
|
216
238
|
# Convert any existing YAML files to MD format
|
|
217
|
-
conversion_results = self._convert_yaml_to_md(
|
|
239
|
+
conversion_results = self._convert_yaml_to_md(agents_dir)
|
|
218
240
|
results["converted"] = conversion_results.get("converted", [])
|
|
219
241
|
|
|
220
242
|
# Load base agent content
|
|
@@ -237,8 +259,14 @@ class AgentDeploymentService:
|
|
|
237
259
|
|
|
238
260
|
# Get all template files
|
|
239
261
|
template_files = list(self.templates_dir.glob("*.json"))
|
|
240
|
-
# Filter out non-agent files
|
|
241
|
-
|
|
262
|
+
# Filter out non-agent files - exclude system files and uppercase special files
|
|
263
|
+
excluded_names = {"__init__", "MEMORIES", "TODOWRITE", "INSTRUCTIONS", "README"}
|
|
264
|
+
template_files = [
|
|
265
|
+
f for f in template_files
|
|
266
|
+
if f.stem not in excluded_names
|
|
267
|
+
and not f.stem.startswith(".")
|
|
268
|
+
and not f.stem.endswith(".backup")
|
|
269
|
+
]
|
|
242
270
|
results["total"] = len(template_files)
|
|
243
271
|
|
|
244
272
|
for template_file in template_files:
|
|
@@ -247,12 +275,22 @@ class AgentDeploymentService:
|
|
|
247
275
|
agent_start_time = time.time()
|
|
248
276
|
|
|
249
277
|
agent_name = template_file.stem
|
|
250
|
-
target_file =
|
|
278
|
+
target_file = agents_dir / f"{agent_name}.md"
|
|
251
279
|
|
|
252
280
|
# Check if agent needs update
|
|
253
281
|
needs_update = force_rebuild
|
|
254
282
|
is_migration = False
|
|
255
|
-
|
|
283
|
+
|
|
284
|
+
# In project deployment mode, always deploy regardless of version
|
|
285
|
+
if deployment_mode == "project":
|
|
286
|
+
if target_file.exists():
|
|
287
|
+
# Check if it's a system agent that we should update
|
|
288
|
+
needs_update = True
|
|
289
|
+
self.logger.debug(f"Project deployment mode: will deploy {agent_name}")
|
|
290
|
+
else:
|
|
291
|
+
needs_update = True
|
|
292
|
+
elif not needs_update and target_file.exists():
|
|
293
|
+
# In update mode, check version compatibility
|
|
256
294
|
needs_update, reason = self._check_agent_needs_update(
|
|
257
295
|
target_file, template_file, base_agent_version
|
|
258
296
|
)
|
|
@@ -264,8 +302,8 @@ class AgentDeploymentService:
|
|
|
264
302
|
else:
|
|
265
303
|
self.logger.info(f"Agent {agent_name} needs update: {reason}")
|
|
266
304
|
|
|
267
|
-
# Skip if exists and doesn't need update
|
|
268
|
-
if target_file.exists() and not needs_update:
|
|
305
|
+
# Skip if exists and doesn't need update (only in update mode)
|
|
306
|
+
if target_file.exists() and not needs_update and deployment_mode != "project":
|
|
269
307
|
results["skipped"].append(agent_name)
|
|
270
308
|
self.logger.debug(f"Skipped up-to-date agent: {agent_name}")
|
|
271
309
|
continue
|
|
@@ -524,26 +562,48 @@ class AgentDeploymentService:
|
|
|
524
562
|
model = (
|
|
525
563
|
template_data.get('capabilities', {}).get('model') or
|
|
526
564
|
template_data.get('configuration_fields', {}).get('model') or
|
|
527
|
-
"
|
|
565
|
+
"sonnet" # Default fallback
|
|
528
566
|
)
|
|
529
567
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
model
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
568
|
+
# Simplify model name for Claude Code
|
|
569
|
+
model_map = {
|
|
570
|
+
'claude-4-sonnet-20250514': 'sonnet',
|
|
571
|
+
'claude-sonnet-4-20250514': 'sonnet',
|
|
572
|
+
'claude-3-opus-20240229': 'opus',
|
|
573
|
+
'claude-3-haiku-20240307': 'haiku',
|
|
574
|
+
'claude-3.5-sonnet': 'sonnet',
|
|
575
|
+
'claude-3-sonnet': 'sonnet'
|
|
576
|
+
}
|
|
577
|
+
model = model_map.get(model, model.split('-')[-1] if '-' in model else model)
|
|
578
|
+
|
|
579
|
+
# Get response format from template or use base agent default
|
|
580
|
+
response_format = template_data.get('response', {}).get('format', 'structured')
|
|
581
|
+
|
|
582
|
+
# Convert lists to space-separated strings for Claude Code compatibility
|
|
583
|
+
tags_str = ' '.join(tags) if isinstance(tags, list) else tags
|
|
584
|
+
tools_str = ', '.join(tools) if isinstance(tools, list) else tools
|
|
585
|
+
|
|
586
|
+
# Build frontmatter with only the fields Claude Code uses
|
|
587
|
+
frontmatter_lines = [
|
|
588
|
+
"---",
|
|
589
|
+
f"name: {agent_name}",
|
|
590
|
+
f"description: {description}",
|
|
591
|
+
f"version: {version_string}",
|
|
592
|
+
f"base_version: {self._format_version_display(base_version)}",
|
|
593
|
+
f"author: claude-mpm", # Identify as system agent for deployment
|
|
594
|
+
f"tools: {tools_str}",
|
|
595
|
+
f"model: {model}"
|
|
596
|
+
]
|
|
597
|
+
|
|
598
|
+
# Add optional fields if present
|
|
599
|
+
if template_data.get('color'):
|
|
600
|
+
frontmatter_lines.append(f"color: {template_data['color']}")
|
|
601
|
+
|
|
602
|
+
frontmatter_lines.append("---")
|
|
603
|
+
frontmatter_lines.append("")
|
|
604
|
+
frontmatter_lines.append("")
|
|
605
|
+
|
|
606
|
+
frontmatter = '\n'.join(frontmatter_lines)
|
|
547
607
|
|
|
548
608
|
# Get the main content (instructions)
|
|
549
609
|
# Check multiple possible locations for instructions
|
|
@@ -874,14 +934,18 @@ temperature: {temperature}"""
|
|
|
874
934
|
"path": str(agent_file)
|
|
875
935
|
}
|
|
876
936
|
|
|
877
|
-
# Extract name and
|
|
937
|
+
# Extract name, version, and base_version from YAML frontmatter
|
|
878
938
|
version_str = None
|
|
939
|
+
base_version_str = None
|
|
879
940
|
for line in lines:
|
|
880
941
|
if line.startswith("name:"):
|
|
881
942
|
agent_info["name"] = line.split(":", 1)[1].strip().strip('"\'')
|
|
882
943
|
elif line.startswith("version:"):
|
|
883
944
|
version_str = line.split(":", 1)[1].strip().strip('"\'')
|
|
884
945
|
agent_info["version"] = version_str
|
|
946
|
+
elif line.startswith("base_version:"):
|
|
947
|
+
base_version_str = line.split(":", 1)[1].strip().strip('"\'')
|
|
948
|
+
agent_info["base_version"] = base_version_str
|
|
885
949
|
|
|
886
950
|
# Check if agent needs migration
|
|
887
951
|
if version_str and self._is_old_version_format(version_str):
|
|
@@ -904,6 +968,87 @@ temperature: {temperature}"""
|
|
|
904
968
|
|
|
905
969
|
return results
|
|
906
970
|
|
|
971
|
+
def deploy_agent(self, agent_name: str, target_dir: Path, force_rebuild: bool = False) -> bool:
|
|
972
|
+
"""
|
|
973
|
+
Deploy a single agent to the specified directory.
|
|
974
|
+
|
|
975
|
+
Args:
|
|
976
|
+
agent_name: Name of the agent to deploy
|
|
977
|
+
target_dir: Target directory for deployment (Path object)
|
|
978
|
+
force_rebuild: Whether to force rebuild even if version is current
|
|
979
|
+
|
|
980
|
+
Returns:
|
|
981
|
+
True if deployment was successful, False otherwise
|
|
982
|
+
|
|
983
|
+
WHY: Single agent deployment because:
|
|
984
|
+
- Users may want to deploy specific agents only
|
|
985
|
+
- Reduces deployment time for targeted updates
|
|
986
|
+
- Enables selective agent management in projects
|
|
987
|
+
|
|
988
|
+
FIXED: Method now correctly handles all internal calls to:
|
|
989
|
+
- _check_agent_needs_update (with 3 arguments)
|
|
990
|
+
- _build_agent_markdown (with 3 arguments including base_agent_data)
|
|
991
|
+
- Properly loads base_agent_data before building agent content
|
|
992
|
+
"""
|
|
993
|
+
try:
|
|
994
|
+
# Find the template file
|
|
995
|
+
template_file = self.templates_dir / f"{agent_name}.json"
|
|
996
|
+
if not template_file.exists():
|
|
997
|
+
self.logger.error(f"Agent template not found: {agent_name}")
|
|
998
|
+
return False
|
|
999
|
+
|
|
1000
|
+
# Ensure target directory exists
|
|
1001
|
+
agents_dir = target_dir / '.claude' / 'agents'
|
|
1002
|
+
agents_dir.mkdir(parents=True, exist_ok=True)
|
|
1003
|
+
|
|
1004
|
+
# Build and deploy the agent
|
|
1005
|
+
target_file = agents_dir / f"{agent_name}.md"
|
|
1006
|
+
|
|
1007
|
+
# Check if update is needed
|
|
1008
|
+
if not force_rebuild and target_file.exists():
|
|
1009
|
+
# Load base agent data for version checking
|
|
1010
|
+
base_agent_data = {}
|
|
1011
|
+
base_agent_version = (0, 0, 0)
|
|
1012
|
+
if self.base_agent_path.exists():
|
|
1013
|
+
try:
|
|
1014
|
+
import json
|
|
1015
|
+
base_agent_data = json.loads(self.base_agent_path.read_text())
|
|
1016
|
+
base_agent_version = self._parse_version(base_agent_data.get('base_version') or base_agent_data.get('version', 0))
|
|
1017
|
+
except Exception as e:
|
|
1018
|
+
self.logger.warning(f"Could not load base agent for version check: {e}")
|
|
1019
|
+
|
|
1020
|
+
needs_update, reason = self._check_agent_needs_update(target_file, template_file, base_agent_version)
|
|
1021
|
+
if not needs_update:
|
|
1022
|
+
self.logger.info(f"Agent {agent_name} is up to date")
|
|
1023
|
+
return True
|
|
1024
|
+
else:
|
|
1025
|
+
self.logger.info(f"Updating agent {agent_name}: {reason}")
|
|
1026
|
+
|
|
1027
|
+
# Load base agent data for building
|
|
1028
|
+
base_agent_data = {}
|
|
1029
|
+
if self.base_agent_path.exists():
|
|
1030
|
+
try:
|
|
1031
|
+
import json
|
|
1032
|
+
base_agent_data = json.loads(self.base_agent_path.read_text())
|
|
1033
|
+
except Exception as e:
|
|
1034
|
+
self.logger.warning(f"Could not load base agent: {e}")
|
|
1035
|
+
|
|
1036
|
+
# Build the agent markdown
|
|
1037
|
+
agent_content = self._build_agent_markdown(agent_name, template_file, base_agent_data)
|
|
1038
|
+
if not agent_content:
|
|
1039
|
+
self.logger.error(f"Failed to build agent content for {agent_name}")
|
|
1040
|
+
return False
|
|
1041
|
+
|
|
1042
|
+
# Write to target file
|
|
1043
|
+
target_file.write_text(agent_content)
|
|
1044
|
+
self.logger.info(f"Successfully deployed agent: {agent_name} to {target_file}")
|
|
1045
|
+
|
|
1046
|
+
return True
|
|
1047
|
+
|
|
1048
|
+
except Exception as e:
|
|
1049
|
+
self.logger.error(f"Failed to deploy agent {agent_name}: {e}")
|
|
1050
|
+
return False
|
|
1051
|
+
|
|
907
1052
|
def list_available_agents(self) -> List[Dict[str, Any]]:
|
|
908
1053
|
"""
|
|
909
1054
|
List available agent templates.
|
|
@@ -918,8 +1063,14 @@ temperature: {temperature}"""
|
|
|
918
1063
|
return agents
|
|
919
1064
|
|
|
920
1065
|
template_files = sorted(self.templates_dir.glob("*.json"))
|
|
921
|
-
# Filter out non-agent files
|
|
922
|
-
|
|
1066
|
+
# Filter out non-agent files - exclude system files and uppercase special files
|
|
1067
|
+
excluded_names = {"__init__", "MEMORIES", "TODOWRITE", "INSTRUCTIONS", "README"}
|
|
1068
|
+
template_files = [
|
|
1069
|
+
f for f in template_files
|
|
1070
|
+
if f.stem not in excluded_names
|
|
1071
|
+
and not f.stem.startswith(".")
|
|
1072
|
+
and not f.stem.endswith(".backup")
|
|
1073
|
+
]
|
|
923
1074
|
|
|
924
1075
|
for template_file in template_files:
|
|
925
1076
|
try:
|