claude-mpm 2.0.0__py3-none-any.whl → 2.1.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.py +4 -34
- claude_mpm/agents/INSTRUCTIONS.md +1 -16
- claude_mpm/agents/templates/research.json +53 -85
- claude_mpm/cli.py +80 -11
- claude_mpm/core/simple_runner.py +45 -5
- claude_mpm/hooks/claude_hooks/hook_handler.py +115 -1
- claude_mpm/services/agent_capabilities_generator.py +182 -0
- claude_mpm/services/agent_deployment.py +228 -37
- claude_mpm/services/deployed_agent_discovery.py +227 -0
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +29 -0
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +29 -7
- claude_mpm/utils/framework_detection.py +39 -0
- {claude_mpm-2.0.0.dist-info → claude_mpm-2.1.1.dist-info}/METADATA +11 -1
- {claude_mpm-2.0.0.dist-info → claude_mpm-2.1.1.dist-info}/RECORD +18 -14
- claude_mpm-2.1.1.dist-info/licenses/LICENSE +21 -0
- {claude_mpm-2.0.0.dist-info → claude_mpm-2.1.1.dist-info}/WHEEL +0 -0
- {claude_mpm-2.0.0.dist-info → claude_mpm-2.1.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-2.0.0.dist-info → claude_mpm-2.1.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""Deployed Agent Discovery Service.
|
|
2
|
+
|
|
3
|
+
This service discovers and analyzes deployed agents in the project,
|
|
4
|
+
handling both new standardized schema and legacy agent formats.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import List, Dict, Any
|
|
9
|
+
import logging
|
|
10
|
+
import json
|
|
11
|
+
|
|
12
|
+
from claude_mpm.core.agent_registry import AgentRegistryAdapter
|
|
13
|
+
from claude_mpm.utils.paths import PathResolver
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DeployedAgentDiscovery:
|
|
19
|
+
"""Discovers and analyzes deployed agents in the project."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, project_root: Path = None):
|
|
22
|
+
"""Initialize the discovery service.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
project_root: Project root path. Defaults to auto-detected root.
|
|
26
|
+
"""
|
|
27
|
+
self.project_root = project_root or PathResolver.get_project_root()
|
|
28
|
+
self.agent_registry = AgentRegistryAdapter()
|
|
29
|
+
logger.debug(f"Initialized DeployedAgentDiscovery with root: {self.project_root}")
|
|
30
|
+
|
|
31
|
+
def discover_deployed_agents(self) -> List[Dict[str, Any]]:
|
|
32
|
+
"""Discover all deployed agents following hierarchy precedence.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
List of agent information dictionaries with standardized fields.
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
# Get effective agents (respects project > user > system precedence)
|
|
39
|
+
agents = self.agent_registry.list_agents()
|
|
40
|
+
|
|
41
|
+
# Handle both dict and list formats
|
|
42
|
+
if isinstance(agents, dict):
|
|
43
|
+
agent_list = list(agents.values())
|
|
44
|
+
else:
|
|
45
|
+
agent_list = list(agents)
|
|
46
|
+
|
|
47
|
+
logger.debug(f"Found {len(agent_list)} entries in registry")
|
|
48
|
+
|
|
49
|
+
deployed_agents = []
|
|
50
|
+
filtered_count = 0
|
|
51
|
+
for agent in agent_list:
|
|
52
|
+
try:
|
|
53
|
+
agent_info = self._extract_agent_info(agent)
|
|
54
|
+
if agent_info and self._is_valid_agent(agent_info):
|
|
55
|
+
deployed_agents.append(agent_info)
|
|
56
|
+
logger.debug(f"Extracted info for agent: {agent_info['id']}")
|
|
57
|
+
elif agent_info:
|
|
58
|
+
filtered_count += 1
|
|
59
|
+
logger.debug(f"Filtered out non-deployable agent: {agent_info.get('id', 'unknown')}")
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.error(f"Failed to extract info from agent {agent}: {e}")
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
logger.info(f"Discovered {len(deployed_agents)} deployable agents from registry ({filtered_count} templates/base agents filtered out)")
|
|
65
|
+
return deployed_agents
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.error(f"Failed to discover deployed agents: {e}")
|
|
69
|
+
# Return empty list on failure to allow graceful degradation
|
|
70
|
+
return []
|
|
71
|
+
|
|
72
|
+
def _extract_agent_info(self, agent) -> Dict[str, Any]:
|
|
73
|
+
"""Extract relevant information from agent definition.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
agent: Agent object from registry (can be dict or object)
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Dictionary with standardized agent information
|
|
80
|
+
"""
|
|
81
|
+
try:
|
|
82
|
+
# Handle dictionary format (current format from registry)
|
|
83
|
+
if isinstance(agent, dict):
|
|
84
|
+
# If we have a path, try to load full agent data from JSON
|
|
85
|
+
agent_path = agent.get('path')
|
|
86
|
+
if agent_path and agent_path.endswith('.json'):
|
|
87
|
+
full_data = self._load_full_agent_data(agent_path)
|
|
88
|
+
if full_data:
|
|
89
|
+
return self._extract_from_json_data(full_data, agent)
|
|
90
|
+
|
|
91
|
+
# Otherwise use basic info from registry
|
|
92
|
+
return {
|
|
93
|
+
'id': agent.get('type', agent.get('name', 'unknown')),
|
|
94
|
+
'name': agent.get('name', 'Unknown'),
|
|
95
|
+
'description': agent.get('description', 'No description available'),
|
|
96
|
+
'specializations': agent.get('specializations', []),
|
|
97
|
+
'capabilities': agent.get('capabilities', {}),
|
|
98
|
+
'source_tier': agent.get('tier', 'system'),
|
|
99
|
+
'tools': agent.get('tools', [])
|
|
100
|
+
}
|
|
101
|
+
# Handle object format with metadata (new standardized schema)
|
|
102
|
+
elif hasattr(agent, 'metadata'):
|
|
103
|
+
return {
|
|
104
|
+
'id': agent.agent_id,
|
|
105
|
+
'name': agent.metadata.name,
|
|
106
|
+
'description': agent.metadata.description,
|
|
107
|
+
'specializations': agent.metadata.specializations,
|
|
108
|
+
'capabilities': getattr(agent, 'capabilities', {}),
|
|
109
|
+
'source_tier': self._determine_source_tier(agent),
|
|
110
|
+
'tools': getattr(agent.configuration, 'tools', []) if hasattr(agent, 'configuration') else []
|
|
111
|
+
}
|
|
112
|
+
else:
|
|
113
|
+
# Legacy object format fallback
|
|
114
|
+
agent_type = getattr(agent, 'type', None)
|
|
115
|
+
agent_name = getattr(agent, 'name', None)
|
|
116
|
+
|
|
117
|
+
# Generate name from type if name not present
|
|
118
|
+
if not agent_name and agent_type:
|
|
119
|
+
agent_name = agent_type.replace('_', ' ').title()
|
|
120
|
+
elif not agent_name:
|
|
121
|
+
agent_name = 'Unknown Agent'
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
'id': getattr(agent, 'agent_id', agent_type or 'unknown'),
|
|
125
|
+
'name': agent_name,
|
|
126
|
+
'description': getattr(agent, 'description', 'No description available'),
|
|
127
|
+
'specializations': getattr(agent, 'specializations', []),
|
|
128
|
+
'capabilities': {},
|
|
129
|
+
'source_tier': self._determine_source_tier(agent),
|
|
130
|
+
'tools': getattr(agent, 'tools', [])
|
|
131
|
+
}
|
|
132
|
+
except Exception as e:
|
|
133
|
+
logger.error(f"Error extracting agent info: {e}")
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
def _load_full_agent_data(self, agent_path: str) -> Dict[str, Any]:
|
|
137
|
+
"""Load full agent data from JSON file.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
agent_path: Path to agent JSON file
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Full agent data dictionary or None if loading fails
|
|
144
|
+
"""
|
|
145
|
+
try:
|
|
146
|
+
path = Path(agent_path)
|
|
147
|
+
if path.exists() and path.suffix == '.json':
|
|
148
|
+
with open(path, 'r') as f:
|
|
149
|
+
return json.load(f)
|
|
150
|
+
except Exception as e:
|
|
151
|
+
logger.warning(f"Failed to load full agent data from {agent_path}: {e}")
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
def _extract_from_json_data(self, json_data: Dict[str, Any], registry_info: Dict[str, Any]) -> Dict[str, Any]:
|
|
155
|
+
"""Extract agent info from full JSON data.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
json_data: Full agent JSON data
|
|
159
|
+
registry_info: Basic info from registry
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Extracted agent information
|
|
163
|
+
"""
|
|
164
|
+
# Extract metadata
|
|
165
|
+
metadata = json_data.get('metadata', {})
|
|
166
|
+
capabilities = json_data.get('capabilities', {})
|
|
167
|
+
configuration = json_data.get('configuration', {})
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
'id': json_data.get('agent_type', registry_info.get('type', 'unknown')),
|
|
171
|
+
'name': metadata.get('name', registry_info.get('name', 'Unknown')),
|
|
172
|
+
'description': metadata.get('description', registry_info.get('description', 'No description available')),
|
|
173
|
+
'specializations': metadata.get('specializations', registry_info.get('specializations', [])),
|
|
174
|
+
'capabilities': capabilities,
|
|
175
|
+
'source_tier': registry_info.get('tier', 'system'),
|
|
176
|
+
'tools': configuration.get('tools', [])
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
def _determine_source_tier(self, agent) -> str:
|
|
180
|
+
"""Determine if agent comes from project, user, or system tier.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
agent: Agent object from registry (can be dict or object)
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Source tier string: 'project', 'user', or 'system'
|
|
187
|
+
"""
|
|
188
|
+
# Handle dictionary format
|
|
189
|
+
if isinstance(agent, dict):
|
|
190
|
+
return agent.get('tier', 'system')
|
|
191
|
+
|
|
192
|
+
# First check if agent has explicit source_tier attribute
|
|
193
|
+
if hasattr(agent, 'source_tier'):
|
|
194
|
+
return agent.source_tier
|
|
195
|
+
|
|
196
|
+
# Try to determine from file path if available
|
|
197
|
+
if hasattr(agent, 'source_path'):
|
|
198
|
+
source_path = str(agent.source_path)
|
|
199
|
+
if '.claude/agents' in source_path:
|
|
200
|
+
return 'project'
|
|
201
|
+
elif str(Path.home()) in source_path:
|
|
202
|
+
return 'user'
|
|
203
|
+
|
|
204
|
+
# Default to system tier
|
|
205
|
+
return 'system'
|
|
206
|
+
|
|
207
|
+
def _is_valid_agent(self, agent_info: Dict[str, Any]) -> bool:
|
|
208
|
+
"""Check if agent is a valid deployable agent (not a template).
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
agent_info: Extracted agent information
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
True if agent is valid, False if it's a template or invalid
|
|
215
|
+
"""
|
|
216
|
+
# Filter out known templates and non-agent files
|
|
217
|
+
invalid_names = ['BASE_AGENT_TEMPLATE', 'INSTRUCTIONS', 'base_agent', 'template']
|
|
218
|
+
|
|
219
|
+
agent_id = agent_info.get('id', '').upper()
|
|
220
|
+
agent_name = agent_info.get('name', '').upper()
|
|
221
|
+
|
|
222
|
+
for invalid in invalid_names:
|
|
223
|
+
if invalid.upper() in agent_id or invalid.upper() in agent_name:
|
|
224
|
+
logger.debug(f"Filtering out template/invalid agent: {agent_info['id']}")
|
|
225
|
+
return False
|
|
226
|
+
|
|
227
|
+
return True
|
|
@@ -8,6 +8,12 @@ import hashlib
|
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
from typing import Dict, List, Optional, Any
|
|
10
10
|
from collections import OrderedDict
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
from claude_mpm.services.deployed_agent_discovery import DeployedAgentDiscovery
|
|
14
|
+
from claude_mpm.services.agent_capabilities_generator import AgentCapabilitiesGenerator
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
11
17
|
|
|
12
18
|
|
|
13
19
|
class ContentAssembler:
|
|
@@ -16,6 +22,9 @@ class ContentAssembler:
|
|
|
16
22
|
def __init__(self):
|
|
17
23
|
"""Initialize content assembler."""
|
|
18
24
|
self.template_variables = {}
|
|
25
|
+
self.agent_discovery = DeployedAgentDiscovery()
|
|
26
|
+
self.capabilities_generator = AgentCapabilitiesGenerator()
|
|
27
|
+
logger.debug("Initialized ContentAssembler with dynamic agent capabilities support")
|
|
19
28
|
|
|
20
29
|
def generate_content_hash(self) -> str:
|
|
21
30
|
"""
|
|
@@ -63,12 +72,32 @@ class ContentAssembler:
|
|
|
63
72
|
"""
|
|
64
73
|
Apply template variable substitution to content.
|
|
65
74
|
|
|
75
|
+
WHY: Enhanced to support dynamic agent capabilities generation.
|
|
76
|
+
- Generates fresh agent capabilities on each call
|
|
77
|
+
- Provides graceful fallback if generation fails
|
|
78
|
+
- Ensures INSTRUCTIONS.md always reflects current deployed agents
|
|
79
|
+
|
|
66
80
|
Args:
|
|
67
81
|
content: Content with template variables
|
|
68
82
|
|
|
69
83
|
Returns:
|
|
70
84
|
str: Content with variables substituted
|
|
71
85
|
"""
|
|
86
|
+
# Check if we need to generate dynamic capabilities
|
|
87
|
+
if "{{capabilities-list}}" in content:
|
|
88
|
+
try:
|
|
89
|
+
# Discover deployed agents
|
|
90
|
+
deployed_agents = self.agent_discovery.discover_deployed_agents()
|
|
91
|
+
# Generate capabilities content
|
|
92
|
+
capabilities_content = self.capabilities_generator.generate_capabilities_section(deployed_agents)
|
|
93
|
+
# Add to template variables
|
|
94
|
+
self.template_variables['capabilities-list'] = capabilities_content
|
|
95
|
+
logger.info(f"Generated dynamic capabilities for {len(deployed_agents)} agents")
|
|
96
|
+
except Exception as e:
|
|
97
|
+
logger.error(f"Failed to generate dynamic capabilities: {e}")
|
|
98
|
+
# Fallback is handled by the generator's internal fallback mechanism
|
|
99
|
+
|
|
100
|
+
# Apply all template variables
|
|
72
101
|
for var_name, var_value in self.template_variables.items():
|
|
73
102
|
placeholder = f"{{{{{var_name}}}}}"
|
|
74
103
|
content = content.replace(placeholder, var_value)
|
|
@@ -35,6 +35,11 @@ class DeploymentManager:
|
|
|
35
35
|
"""
|
|
36
36
|
Deploy generated content to a parent directory.
|
|
37
37
|
|
|
38
|
+
WHY: Enhanced to ensure fresh agent capabilities generation on each deployment.
|
|
39
|
+
- Checks for template variables that need processing
|
|
40
|
+
- Re-processes content to get current deployed agents
|
|
41
|
+
- Ensures INSTRUCTIONS.md always reflects latest agent configuration
|
|
42
|
+
|
|
38
43
|
Args:
|
|
39
44
|
content: Content to deploy
|
|
40
45
|
parent_path: Path to parent directory
|
|
@@ -52,16 +57,33 @@ class DeploymentManager:
|
|
|
52
57
|
target_file = parent_path / "INSTRUCTIONS.md"
|
|
53
58
|
# TODO: Make this configurable via parameter
|
|
54
59
|
|
|
60
|
+
# Check if content contains template variables that need processing
|
|
61
|
+
if '{{capabilities-list}}' in content:
|
|
62
|
+
# Content needs processing - let ContentAssembler handle it
|
|
63
|
+
from .content_assembler import ContentAssembler
|
|
64
|
+
assembler = ContentAssembler()
|
|
65
|
+
|
|
66
|
+
# Re-process content to get fresh agent data
|
|
67
|
+
# Pass content as a single section to preserve structure
|
|
68
|
+
processed_content = assembler.apply_template_variables(content)
|
|
69
|
+
content = processed_content
|
|
70
|
+
|
|
55
71
|
# Validate content before deployment
|
|
56
|
-
|
|
57
|
-
if
|
|
58
|
-
|
|
72
|
+
# Skip validation for INSTRUCTIONS.md format (different from CLAUDE.md)
|
|
73
|
+
if "<!-- FRAMEWORK_VERSION:" in content and "# Claude Multi-Agent Project Manager Instructions" in content:
|
|
74
|
+
# This is INSTRUCTIONS.md format, skip CLAUDE.md validation
|
|
75
|
+
pass
|
|
76
|
+
else:
|
|
77
|
+
# This is CLAUDE.md format, validate normally
|
|
78
|
+
is_valid, issues = self.validator.validate_content(content)
|
|
79
|
+
if not is_valid:
|
|
80
|
+
return False, f"Validation failed: {'; '.join(issues)}"
|
|
59
81
|
|
|
60
82
|
# Check if file exists and compare versions
|
|
61
83
|
if target_file.exists() and not force:
|
|
62
84
|
with open(target_file, 'r') as f:
|
|
63
85
|
existing_content = f.read()
|
|
64
|
-
existing_fw_ver
|
|
86
|
+
existing_fw_ver = self.version_manager.parse_current_version(existing_content)
|
|
65
87
|
|
|
66
88
|
if existing_fw_ver == self.version_manager.framework_version:
|
|
67
89
|
return True, f"Version {existing_fw_ver} already deployed"
|
|
@@ -76,8 +98,8 @@ class DeploymentManager:
|
|
|
76
98
|
f.write(content)
|
|
77
99
|
|
|
78
100
|
# Get version info for success message
|
|
79
|
-
fw_ver
|
|
80
|
-
version_str =
|
|
101
|
+
fw_ver = self.version_manager.parse_current_version(content)
|
|
102
|
+
version_str = fw_ver
|
|
81
103
|
|
|
82
104
|
return True, f"Successfully deployed version {version_str}"
|
|
83
105
|
except Exception as e:
|
|
@@ -101,7 +123,7 @@ class DeploymentManager:
|
|
|
101
123
|
try:
|
|
102
124
|
with open(target_file, 'r') as f:
|
|
103
125
|
existing_content = f.read()
|
|
104
|
-
existing_fw_ver
|
|
126
|
+
existing_fw_ver = self.version_manager.parse_current_version(existing_content)
|
|
105
127
|
|
|
106
128
|
if existing_fw_ver != self.version_manager.framework_version:
|
|
107
129
|
return True, f"Version mismatch: {existing_fw_ver} vs {self.version_manager.framework_version}"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Framework source directory detection utilities.
|
|
2
|
+
|
|
3
|
+
WHY: This module provides utilities to detect if we're in the framework source directory
|
|
4
|
+
to prevent accidental overwrites of the template files during deployment.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Tuple, List
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def is_framework_source_directory(path: Path) -> Tuple[bool, List[str]]:
|
|
12
|
+
"""
|
|
13
|
+
Check if the given path is the framework source directory.
|
|
14
|
+
|
|
15
|
+
WHY: We need to prevent deployment to the framework source directory itself
|
|
16
|
+
to avoid overwriting template files.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
path: Path to check
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Tuple of (is_framework_source, list of detected markers)
|
|
23
|
+
"""
|
|
24
|
+
markers = []
|
|
25
|
+
|
|
26
|
+
# Check for framework source markers
|
|
27
|
+
if (path / "src" / "claude_mpm").exists():
|
|
28
|
+
markers.append("src/claude_mpm")
|
|
29
|
+
|
|
30
|
+
if (path / "pyproject.toml").exists():
|
|
31
|
+
markers.append("pyproject.toml")
|
|
32
|
+
|
|
33
|
+
if (path / "src" / "claude_mpm" / "agents" / "INSTRUCTIONS.md").exists():
|
|
34
|
+
markers.append("framework INSTRUCTIONS.md template")
|
|
35
|
+
|
|
36
|
+
# If we have multiple markers, it's likely the framework source
|
|
37
|
+
is_framework = len(markers) >= 2
|
|
38
|
+
|
|
39
|
+
return is_framework, markers
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-mpm
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Claude Multi-agent Project Manager - Clean orchestration with ticket management
|
|
5
5
|
Home-page: https://github.com/bobmatnyc/claude-mpm
|
|
6
6
|
Author: Claude MPM Team
|
|
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Requires-Python: >=3.8
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
21
22
|
Requires-Dist: ai-trackdown-pytools>=1.2.0
|
|
22
23
|
Requires-Dist: pyyaml>=6.0
|
|
23
24
|
Requires-Dist: python-dotenv>=0.19.0
|
|
@@ -40,6 +41,7 @@ Requires-Dist: flake8; extra == "dev"
|
|
|
40
41
|
Requires-Dist: mypy; extra == "dev"
|
|
41
42
|
Dynamic: author-email
|
|
42
43
|
Dynamic: home-page
|
|
44
|
+
Dynamic: license-file
|
|
43
45
|
Dynamic: requires-python
|
|
44
46
|
|
|
45
47
|
# Claude MPM - Multi-Agent Project Manager
|
|
@@ -194,6 +196,7 @@ Claude MPM provides a modular framework for extending Claude's capabilities:
|
|
|
194
196
|
- Dynamic agent discovery and registration
|
|
195
197
|
- Template-based agent definitions
|
|
196
198
|
- Extensible agent architecture
|
|
199
|
+
- **Dynamic Capabilities**: Agent documentation automatically generated from deployed agents
|
|
197
200
|
|
|
198
201
|
### Hook System
|
|
199
202
|
- Pre and post-processing hooks
|
|
@@ -213,6 +216,13 @@ Claude MPM provides a modular framework for extending Claude's capabilities:
|
|
|
213
216
|
- Organized log structure
|
|
214
217
|
- Performance monitoring
|
|
215
218
|
|
|
219
|
+
### Security Features
|
|
220
|
+
- **File System Protection**: Automatic sandboxing prevents file operations outside the working directory
|
|
221
|
+
- **Path Traversal Prevention**: Blocks attempts to escape the project directory using `..` or symlinks
|
|
222
|
+
- **Write Operation Control**: All write operations are validated while read operations remain unrestricted
|
|
223
|
+
- **Transparent Security**: Zero-configuration security that works automatically in the background
|
|
224
|
+
- **Comprehensive Logging**: All security events are logged for audit purposes
|
|
225
|
+
|
|
216
226
|
## Installation
|
|
217
227
|
|
|
218
228
|
### Other Installation Methods
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
claude_mpm/__init__.py,sha256=sAbTZkHe3vWYAKDWdGyGVue5zwLD7nCOHZwZrLALM8A,395
|
|
2
2
|
claude_mpm/__main__.py,sha256=smBw-5J3nf5s6GgQjj384GUr28YotIX-WNOxqpP0wnE,310
|
|
3
|
-
claude_mpm/_version.py,sha256=
|
|
4
|
-
claude_mpm/cli.py,sha256=
|
|
3
|
+
claude_mpm/_version.py,sha256=HBkjBFMyGB0Jyz0lTkWhWTEalldfBZqkqfHkyuIxBJk,159
|
|
4
|
+
claude_mpm/cli.py,sha256=_6tUSY0FqBN6cHR6awuXgqm2-Hlh-L508rB-RDgflPU,26036
|
|
5
5
|
claude_mpm/cli_enhancements.py,sha256=nwdOrbXITRqvcq_vrJtPKW1GDS7dLIG4UqjoUet2vR0,10890
|
|
6
6
|
claude_mpm/cli_main.py,sha256=KCAe-ws73NrIg5qmFhPdZ1a4uoiaEZ-lldYzQ6KfnJg,306
|
|
7
7
|
claude_mpm/constants.py,sha256=5AG5hgBxOC7gMNHDx0lAhS-FQ8gXhtGtqJ9Moj3S6ro,4044
|
|
8
8
|
claude_mpm/init.py,sha256=gOreOf7BLXkT0_HrQk_As4Kz1OT_NJG_RG0i0hbY0z0,8088
|
|
9
9
|
claude_mpm/agents/BASE_AGENT_TEMPLATE.md,sha256=TYgSd9jNBMWp4mAOBUl9dconX4RcGbvmMEScRy5uyko,3343
|
|
10
|
-
claude_mpm/agents/INSTRUCTIONS.md,sha256=
|
|
10
|
+
claude_mpm/agents/INSTRUCTIONS.md,sha256=X6bOhSII3pZVZh_Vw_zMYRdfLtyl5Mmf_jhrVYfNxFs,7200
|
|
11
11
|
claude_mpm/agents/__init__.py,sha256=r-p7ervzjLPD7_8dm2tXX_fwvdTZy6KwKA03ofxN3sA,3275
|
|
12
12
|
claude_mpm/agents/agent-template.yaml,sha256=koKJn8MCAJx0QNQMHouvIZrwvw5qjPV0U-aV-YVyk6s,2036
|
|
13
13
|
claude_mpm/agents/agent_loader.py,sha256=aZiTX8u0__8ueHzwney98PHfVMSkrpF3Nh-DoFlso74,18675
|
|
@@ -23,7 +23,7 @@ claude_mpm/agents/templates/documentation.json,sha256=W6gF0p2XpXrYPng7aUjbpE0Z3I
|
|
|
23
23
|
claude_mpm/agents/templates/engineer.json,sha256=vRAr9sEgKt7_IVg2e3iZmLQFQHTzjgM-YQepS-TL6rY,8829
|
|
24
24
|
claude_mpm/agents/templates/ops.json,sha256=mNoS_OS_e4Ht2drx3d7H0I18x38LX16E-mhsJn95ydQ,2899
|
|
25
25
|
claude_mpm/agents/templates/qa.json,sha256=qEsJzkXbqM4pAdl4vyW1HeaQME5SvnIk4XttPWI2Dhw,2942
|
|
26
|
-
claude_mpm/agents/templates/research.json,sha256=
|
|
26
|
+
claude_mpm/agents/templates/research.json,sha256=nwqExnDvMVGih7RGzbwpYGBb1NbAdHRq3L03S4QtzKs,10180
|
|
27
27
|
claude_mpm/agents/templates/security.json,sha256=T0hRMeHq54ryblR2QsBFBJtNx-mYR3VEtwQYOvx4EOU,2945
|
|
28
28
|
claude_mpm/agents/templates/version_control.json,sha256=RsV0WLs58boDOzurwB75Zdol4uXf_Za0VHOqQ3Og2Rw,2923
|
|
29
29
|
claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json,sha256=lLso4RHXVTQmX4A1XwF84kT59zZDblPO1xCgBj4S4x8,5060
|
|
@@ -62,7 +62,7 @@ claude_mpm/core/minimal_framework_loader.py,sha256=liYS4IyuW_aFK7yhRDZwTwT-3q09f
|
|
|
62
62
|
claude_mpm/core/mixins.py,sha256=rTEH-7FDpNiLB8oo6mSb0CLarJklv4fDJw1xM-gr5wI,5599
|
|
63
63
|
claude_mpm/core/service_registry.py,sha256=wKJUO1g4UFA4dUpE3RkIYz1Ek8kIh4XfvU1kFeLCl2Q,10529
|
|
64
64
|
claude_mpm/core/session_manager.py,sha256=3rO4KGZp8Qd_cUw6OWv4jyxGCUaL_MNPgCCpnwQt12A,6581
|
|
65
|
-
claude_mpm/core/simple_runner.py,sha256=
|
|
65
|
+
claude_mpm/core/simple_runner.py,sha256=DRChSTgPt6g0zQUZe9cEpH9ky2aZyLnlH0bhOI63vfc,24645
|
|
66
66
|
claude_mpm/core/tool_access_control.py,sha256=htZbDhC8s7D7BVqfmk0BwRrYJnlnUAk8_NeJKOaeNlg,6632
|
|
67
67
|
claude_mpm/generators/__init__.py,sha256=l53aBn6kBQSDz3b6bZkMCJBcEmYnV9hHEZq8LKzXgH8,152
|
|
68
68
|
claude_mpm/generators/agent_profile_generator.py,sha256=2HjOscogSyvrtQj8KwdgNPS6Ym_QvgX1BMeauQZewZA,5751
|
|
@@ -81,7 +81,7 @@ claude_mpm/hooks/builtin/ticket_extraction_hook_example.py,sha256=4wNhS2tFUXgdcv
|
|
|
81
81
|
claude_mpm/hooks/builtin/todo_agent_prefix_hook.py,sha256=v_4w2vcZIt0bkZxqdHmgtN79yHZ1gviuhhBws0FHpIQ,10226
|
|
82
82
|
claude_mpm/hooks/builtin/workflow_start_hook.py,sha256=EQrtYD9qLMLSYPl3oQinEheZAJ2i5EO_h2jhhR8lmt0,7490
|
|
83
83
|
claude_mpm/hooks/claude_hooks/__init__.py,sha256=bMUwt2RzDGAcEbtDMA7vWS1uJsauOY0OixIe4pHwgQ0,129
|
|
84
|
-
claude_mpm/hooks/claude_hooks/hook_handler.py,sha256=
|
|
84
|
+
claude_mpm/hooks/claude_hooks/hook_handler.py,sha256=r1OqgtXw04u47FVV8Ks2ppHh8hC4xSQrj3Q3hWHuHxs,21627
|
|
85
85
|
claude_mpm/hooks/claude_hooks/hook_wrapper.sh,sha256=6n0-G317jIrPuNRGnAyFvBbNM4gVzKEat_WSbpvKN-g,1742
|
|
86
86
|
claude_mpm/orchestration/SUBPROCESS_DESIGN.md,sha256=YwToiT1_NXblv1XIWhWPNc2uKzDvqY2E_Nix8qK7qk0,2136
|
|
87
87
|
claude_mpm/orchestration/__init__.py,sha256=C-cwldtfBCgV19mKnJa5U1XiKw1rAZvx-kK61nIdcao,205
|
|
@@ -101,13 +101,15 @@ claude_mpm/schemas/agent_schema.json,sha256=xPqYaZ8VNFyabdpEL91kufKPsWk36v6hYbqo
|
|
|
101
101
|
claude_mpm/scripts/__init__.py,sha256=M2n9fQeyfILC8gogXvJv6ixnu7hwpqLEqLWJRaUN0MU,37
|
|
102
102
|
claude_mpm/scripts/ticket.py,sha256=GmFimtTJxc927cCzJvvJH3gvoxXQtAB-W-xnuclcvNs,9350
|
|
103
103
|
claude_mpm/services/__init__.py,sha256=-EBm07Lh9mjcofiQHCqyCCQJMLi9akVArPlz8i_kEOo,226
|
|
104
|
-
claude_mpm/services/
|
|
104
|
+
claude_mpm/services/agent_capabilities_generator.py,sha256=K0oO2N4yjRXmf27FF6dbWCXcugT-7uGWz5p0p0APDSg,6658
|
|
105
|
+
claude_mpm/services/agent_deployment.py,sha256=23nvYNyDj7QXxaiDsbwhuYhNDAv9vJ-KJI-SEHWcH5I,49837
|
|
105
106
|
claude_mpm/services/agent_lifecycle_manager.py,sha256=VZBVONepomFpMqL7soaXIawS4NoaltFWKed7tU6bS3w,39057
|
|
106
107
|
claude_mpm/services/agent_management_service.py,sha256=eX5n6w17b9urcogVdr4V-kXcuo7yyjORTrIihjF8PeQ,22853
|
|
107
108
|
claude_mpm/services/agent_modification_tracker.py,sha256=7FRDXuCNANUnLatCgtBArG-AxZNtKbGQjgCKjnzmJ80,34050
|
|
108
109
|
claude_mpm/services/agent_profile_loader.py,sha256=4D1Xj0vgqV8wN7Y3r8lijh7ghy5cVGU5t5s931sVqGc,23133
|
|
109
110
|
claude_mpm/services/agent_registry.py,sha256=vn8CEW0vppj_0EY2NofmNRZEnpV70mlWiX2kAViFDRg,24374
|
|
110
111
|
claude_mpm/services/base_agent_manager.py,sha256=WEcfzdMaFXmXUSoEYEPNeGu8dvqjIv53zyUU0ITrhsM,14987
|
|
112
|
+
claude_mpm/services/deployed_agent_discovery.py,sha256=YSycVWvxbf5QtM1FkcyVKqqA0ZG4npvTTv6jexeALkE,9591
|
|
111
113
|
claude_mpm/services/framework_agent_loader.py,sha256=QdRSYRurYF3YbAXJwIGei71BffD5AqOVcV3ktRPdk7g,14018
|
|
112
114
|
claude_mpm/services/framework_claude_md_generator.py,sha256=3kHmkRLHTex6HFZ4DhbLVQb48j-5dAoy1q6UW1Qf7U8,22914
|
|
113
115
|
claude_mpm/services/shared_prompt_cache.py,sha256=D04lrRWyg0lHyqGcAHy7IYvRHRKSg6EOpAJwBUPa2wk,29890
|
|
@@ -116,9 +118,9 @@ claude_mpm/services/ticket_manager_di.py,sha256=pIsIGncbboKzBYSRQTO7ZX5MuQzV6iFf
|
|
|
116
118
|
claude_mpm/services/ticketing_service_original.py,sha256=Dg3TYKsy0Z3JCqV5rlMBXeMrhrkAGxOgAMUNRZtJIhw,16680
|
|
117
119
|
claude_mpm/services/framework_claude_md_generator/README.md,sha256=_-ty72t2afPagDVVUEizPkhs4BYkCeqCnZDNPgZAYtY,3511
|
|
118
120
|
claude_mpm/services/framework_claude_md_generator/__init__.py,sha256=OtnwxLiJektfFtsKdkHM1X27rKkFiNd_rcf4843ziKw,7334
|
|
119
|
-
claude_mpm/services/framework_claude_md_generator/content_assembler.py,sha256=
|
|
121
|
+
claude_mpm/services/framework_claude_md_generator/content_assembler.py,sha256=CZlw84bBWjvK68VQhjAdLnMxXBXipZtcdyPtYWLdrKo,6590
|
|
120
122
|
claude_mpm/services/framework_claude_md_generator/content_validator.py,sha256=89QSGil9_NL15ci3HsVVQmfg7MZvsJS3i_ELP5yD-gA,4583
|
|
121
|
-
claude_mpm/services/framework_claude_md_generator/deployment_manager.py,sha256=
|
|
123
|
+
claude_mpm/services/framework_claude_md_generator/deployment_manager.py,sha256=G0-JBMf7WH8VScCVUbXxSpmxBP_Qy_UX4TEgc4ajHFk,6187
|
|
122
124
|
claude_mpm/services/framework_claude_md_generator/section_manager.py,sha256=KbipmnUg7FD3YnVdUj4Ttrm3HMmWrDnjWC6fKX5D3tI,3222
|
|
123
125
|
claude_mpm/services/framework_claude_md_generator/version_manager.py,sha256=VpJMieGEVkkKC6vyhHxKcUjPNzxBuPwD61lTyPJwxXM,4284
|
|
124
126
|
claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py,sha256=HIIW34LgzTJk_ne4qoNxXgYZp3EhEisJR0SjfNjPG7o,3486
|
|
@@ -158,14 +160,16 @@ claude_mpm/ui/terminal_ui.py,sha256=E_M-L-6EuGp6L8pRpaWEhTqf-ddDXhZp5W85D0XNRPw,
|
|
|
158
160
|
claude_mpm/utils/__init__.py,sha256=E8Hvv6ykL6rnnc8-YmfoGNpRCZbcIirxcFuNz7YvDIg,346
|
|
159
161
|
claude_mpm/utils/config_manager.py,sha256=TlekZYIWOz_ouWHQU4Gc-zckhoFK9AqA25b6A_XZdDc,16412
|
|
160
162
|
claude_mpm/utils/error_handler.py,sha256=W_Zc0FrKudpXvxT66Oxx0v8WkR8HA2KlynzzGy3eGcU,8168
|
|
163
|
+
claude_mpm/utils/framework_detection.py,sha256=nzs1qRZK9K-zT0382z1FpGDvgzUNrUg8rBL-O_WLq-Q,1217
|
|
161
164
|
claude_mpm/utils/import_migration_example.py,sha256=W4a4XH3FY_VBB00BB8Lae2aRPM021PxLHzdUfEs0B5w,2463
|
|
162
165
|
claude_mpm/utils/imports.py,sha256=wX-SOXUHbemx01MHRGQpVwajzXH6qYdQkYNFCIbb2mw,6851
|
|
163
166
|
claude_mpm/utils/path_operations.py,sha256=6pLMnAWBVzHkgp6JyQHmHbGD-dWn-nX21yV4E_eT-kM,11614
|
|
164
167
|
claude_mpm/utils/paths.py,sha256=Xv0SZWdZRkRjN9e6clBcA165ya00GNQxt7SwMz51tfA,10153
|
|
165
168
|
claude_mpm/validation/__init__.py,sha256=bJ19g9lnk7yIjtxzN8XPegp87HTFBzCrGQOpFgRTf3g,155
|
|
166
169
|
claude_mpm/validation/agent_validator.py,sha256=gxBn3m3NyJiHymIoNGUBk4dzRjIrw3TVL6lWPpmZmW8,11693
|
|
167
|
-
claude_mpm-2.
|
|
168
|
-
claude_mpm-2.
|
|
169
|
-
claude_mpm-2.
|
|
170
|
-
claude_mpm-2.
|
|
171
|
-
claude_mpm-2.
|
|
170
|
+
claude_mpm-2.1.0.dist-info/licenses/LICENSE,sha256=cSdDfXjoTVhstrERrqme4zgxAu4GubU22zVEHsiXGxs,1071
|
|
171
|
+
claude_mpm-2.1.0.dist-info/METADATA,sha256=_HnaGOf8sfNGPZB7-MyF7vlxr7aIrZAeNJk4nQDWA4k,13906
|
|
172
|
+
claude_mpm-2.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
173
|
+
claude_mpm-2.1.0.dist-info/entry_points.txt,sha256=PknO31um7d8bt6GjOiVeYpdJpjND0_C1z-LQfY6UfiU,142
|
|
174
|
+
claude_mpm-2.1.0.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
|
175
|
+
claude_mpm-2.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Claude MPM Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|