claude-mpm 5.6.11__py3-none-any.whl → 5.6.13__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/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +22 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
- claude_mpm/skills/__init__.py +2 -1
- claude_mpm/skills/registry.py +295 -90
- {claude_mpm-5.6.11.dist-info → claude_mpm-5.6.13.dist-info}/METADATA +4 -2
- {claude_mpm-5.6.11.dist-info → claude_mpm-5.6.13.dist-info}/RECORD +15 -14
- {claude_mpm-5.6.11.dist-info → claude_mpm-5.6.13.dist-info}/WHEEL +0 -0
- {claude_mpm-5.6.11.dist-info → claude_mpm-5.6.13.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.6.11.dist-info → claude_mpm-5.6.13.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.6.11.dist-info → claude_mpm-5.6.13.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.6.11.dist-info → claude_mpm-5.6.13.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.6.
|
|
1
|
+
5.6.13
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -126,6 +126,15 @@ class EventHandlers:
|
|
|
126
126
|
# Response tracking is optional - silently continue if it fails
|
|
127
127
|
pass
|
|
128
128
|
|
|
129
|
+
# Record user message for auto-pause if active
|
|
130
|
+
auto_pause = getattr(self.hook_handler, "auto_pause_handler", None)
|
|
131
|
+
if auto_pause and auto_pause.is_pause_active():
|
|
132
|
+
try:
|
|
133
|
+
auto_pause.on_user_message(prompt)
|
|
134
|
+
except Exception as e:
|
|
135
|
+
if DEBUG:
|
|
136
|
+
_log(f"Auto-pause user message recording error: {e}")
|
|
137
|
+
|
|
129
138
|
# Emit normalized event (namespace no longer needed with normalized events)
|
|
130
139
|
self.hook_handler._emit_socketio_event("", "user_prompt", prompt_data)
|
|
131
140
|
|
|
@@ -603,6 +612,19 @@ class EventHandlers:
|
|
|
603
612
|
if DEBUG:
|
|
604
613
|
_log(f"Auto-pause error in handle_stop_fast: {e}")
|
|
605
614
|
|
|
615
|
+
# Finalize pause session if active
|
|
616
|
+
try:
|
|
617
|
+
if auto_pause.is_pause_active():
|
|
618
|
+
session_file = auto_pause.on_session_end()
|
|
619
|
+
if session_file:
|
|
620
|
+
if DEBUG:
|
|
621
|
+
_log(
|
|
622
|
+
f"✅ Auto-pause session finalized: {session_file.name}"
|
|
623
|
+
)
|
|
624
|
+
except Exception as e:
|
|
625
|
+
if DEBUG:
|
|
626
|
+
_log(f"❌ Failed to finalize auto-pause session: {e}")
|
|
627
|
+
|
|
606
628
|
# Track response if enabled
|
|
607
629
|
try:
|
|
608
630
|
rtm = getattr(self.hook_handler, "response_tracking_manager", None)
|
|
Binary file
|
claude_mpm/skills/__init__.py
CHANGED
|
@@ -24,7 +24,7 @@ Legacy System (maintained for compatibility):
|
|
|
24
24
|
from .agent_skills_injector import AgentSkillsInjector
|
|
25
25
|
|
|
26
26
|
# Legacy System (maintained for compatibility)
|
|
27
|
-
from .registry import Skill, SkillsRegistry, get_registry
|
|
27
|
+
from .registry import Skill, SkillsRegistry, get_registry, validate_agentskills_spec
|
|
28
28
|
from .skill_manager import SkillManager
|
|
29
29
|
from .skills_registry import SkillsRegistry as SkillsRegistryHelper
|
|
30
30
|
from .skills_service import SkillsService
|
|
@@ -39,4 +39,5 @@ __all__ = [
|
|
|
39
39
|
# New Skills Integration System
|
|
40
40
|
"SkillsService",
|
|
41
41
|
"get_registry",
|
|
42
|
+
"validate_agentskills_spec",
|
|
42
43
|
]
|
claude_mpm/skills/registry.py
CHANGED
|
@@ -14,35 +14,144 @@ logger = get_logger(__name__)
|
|
|
14
14
|
|
|
15
15
|
@dataclass
|
|
16
16
|
class Skill:
|
|
17
|
-
"""Represents a skill that can be used by agents.
|
|
18
|
-
|
|
17
|
+
"""Represents a skill that can be used by agents.
|
|
18
|
+
|
|
19
|
+
Supports agentskills.io specification with backward compatibility for legacy claude-mpm format.
|
|
20
|
+
|
|
21
|
+
Spec fields (agentskills.io):
|
|
22
|
+
- name: Required, 1-64 chars, lowercase alphanumeric + hyphens
|
|
23
|
+
- description: Required, 1-1024 chars
|
|
24
|
+
- license: Optional, license name or reference
|
|
25
|
+
- compatibility: Optional, max 500 chars, environment requirements
|
|
26
|
+
- metadata: Optional, key-value mapping for arbitrary data
|
|
27
|
+
- allowed_tools: Optional, list of pre-approved tools
|
|
28
|
+
|
|
29
|
+
Internal fields:
|
|
30
|
+
- path: Path to skill file
|
|
31
|
+
- content: Skill content (markdown)
|
|
32
|
+
- source: Origin of skill ('bundled', 'user', 'project', 'pm')
|
|
33
|
+
- version: Skill version (from metadata.version or top-level)
|
|
34
|
+
- skill_id: Internal ID (defaults to name)
|
|
35
|
+
- agent_types: Which agent types can use this skill
|
|
36
|
+
- updated_at: Last update timestamp (from metadata.updated)
|
|
37
|
+
- tags: Tags for discovery (from metadata.tags or top-level)
|
|
38
|
+
|
|
39
|
+
Claude-mpm extensions (preserved for backward compat):
|
|
40
|
+
- category: Skill category for organization
|
|
41
|
+
- toolchain: Associated toolchain (python, javascript, etc.)
|
|
42
|
+
- progressive_disclosure: Progressive disclosure configuration
|
|
43
|
+
- user_invocable: Whether skill can be manually invoked
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
# Core spec fields (agentskills.io)
|
|
19
47
|
name: str
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
48
|
+
description: str
|
|
49
|
+
license: Optional[str] = None
|
|
50
|
+
compatibility: Optional[str] = None
|
|
51
|
+
metadata: Dict[str, Any] = None
|
|
52
|
+
allowed_tools: List[str] = None
|
|
53
|
+
|
|
54
|
+
# Internal fields (not in frontmatter spec)
|
|
55
|
+
path: Path = None
|
|
56
|
+
content: str = ""
|
|
57
|
+
source: str = "bundled" # 'bundled', 'user', 'project', 'pm'
|
|
58
|
+
|
|
59
|
+
# Derived fields (from metadata or fallback)
|
|
60
|
+
version: str = "0.1.0" # From metadata.version or top-level
|
|
61
|
+
skill_id: str = "" # Internal ID (defaults to name)
|
|
30
62
|
agent_types: List[str] = None # Which agent types can use this skill
|
|
63
|
+
updated_at: Optional[str] = None # From metadata.updated
|
|
64
|
+
tags: List[str] = None # From metadata.tags or top-level
|
|
31
65
|
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
66
|
+
# Claude-mpm extensions (preserved for backward compat)
|
|
67
|
+
category: Optional[str] = None
|
|
68
|
+
toolchain: Optional[str] = None
|
|
69
|
+
progressive_disclosure: Optional[Dict[str, Any]] = None
|
|
70
|
+
user_invocable: bool = False
|
|
35
71
|
|
|
36
72
|
def __post_init__(self):
|
|
37
73
|
"""Initialize default values if not provided."""
|
|
74
|
+
if self.metadata is None:
|
|
75
|
+
self.metadata = {}
|
|
38
76
|
if self.agent_types is None:
|
|
39
77
|
self.agent_types = []
|
|
40
78
|
if self.tags is None:
|
|
41
79
|
self.tags = []
|
|
80
|
+
if self.allowed_tools is None:
|
|
81
|
+
self.allowed_tools = []
|
|
42
82
|
if not self.skill_id:
|
|
43
83
|
self.skill_id = self.name
|
|
44
84
|
|
|
45
85
|
|
|
86
|
+
def validate_agentskills_spec(skill: Skill) -> tuple[bool, List[str]]:
|
|
87
|
+
"""Validate skill against agentskills.io specification.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
skill: Skill object to validate
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Tuple of (is_valid, list_of_warnings)
|
|
94
|
+
- is_valid: True if skill meets spec requirements
|
|
95
|
+
- warnings: List of warning messages for spec violations
|
|
96
|
+
"""
|
|
97
|
+
warnings = []
|
|
98
|
+
|
|
99
|
+
# Validate name (required field)
|
|
100
|
+
if not skill.name:
|
|
101
|
+
warnings.append("Missing required field: name")
|
|
102
|
+
return False, warnings
|
|
103
|
+
|
|
104
|
+
# Validate name format: lowercase alphanumeric + hyphens, no leading/trailing hyphens
|
|
105
|
+
if not re.match(r"^[a-z0-9]+(-[a-z0-9]+)*$", skill.name):
|
|
106
|
+
warnings.append(
|
|
107
|
+
f"Invalid name format: '{skill.name}' (must be lowercase alphanumeric with hyphens, "
|
|
108
|
+
"no leading/trailing hyphens, no consecutive hyphens)"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Validate name length (max 64 chars)
|
|
112
|
+
if len(skill.name) > 64:
|
|
113
|
+
warnings.append(
|
|
114
|
+
f"Name too long: {len(skill.name)} chars (max 64 per agentskills.io spec)"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Validate description (required field)
|
|
118
|
+
if not skill.description:
|
|
119
|
+
warnings.append("Missing required field: description")
|
|
120
|
+
return False, warnings
|
|
121
|
+
|
|
122
|
+
# Validate description length (1-1024 chars)
|
|
123
|
+
desc_len = len(skill.description)
|
|
124
|
+
if desc_len < 1 or desc_len > 1024:
|
|
125
|
+
warnings.append(
|
|
126
|
+
f"Description length {desc_len} chars is outside spec range (1-1024 chars)"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Validate compatibility length (max 500 chars)
|
|
130
|
+
if skill.compatibility and len(skill.compatibility) > 500:
|
|
131
|
+
warnings.append(
|
|
132
|
+
f"Compatibility field too long: {len(skill.compatibility)} chars (max 500)"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Validate metadata is dict
|
|
136
|
+
if skill.metadata is not None and not isinstance(skill.metadata, dict):
|
|
137
|
+
warnings.append("Metadata must be a key-value mapping (dict)")
|
|
138
|
+
|
|
139
|
+
# Check for spec-compliant metadata structure
|
|
140
|
+
spec_metadata_fields = ["version", "author", "updated", "tags"]
|
|
141
|
+
for field in spec_metadata_fields:
|
|
142
|
+
if hasattr(skill, field):
|
|
143
|
+
field_value = getattr(skill, field)
|
|
144
|
+
if field_value and field not in skill.metadata:
|
|
145
|
+
warnings.append(
|
|
146
|
+
f"Field '{field}' should be in metadata block per agentskills.io spec "
|
|
147
|
+
f"(found as top-level field)"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Valid if no errors (only warnings allowed)
|
|
151
|
+
is_valid = len(warnings) == 0 or all("should be in metadata" in w for w in warnings)
|
|
152
|
+
return is_valid, warnings
|
|
153
|
+
|
|
154
|
+
|
|
46
155
|
class SkillsRegistry:
|
|
47
156
|
"""Registry for managing skills across all tiers."""
|
|
48
157
|
|
|
@@ -54,7 +163,10 @@ class SkillsRegistry:
|
|
|
54
163
|
self._load_project_skills()
|
|
55
164
|
|
|
56
165
|
def _parse_skill_frontmatter(self, content: str) -> Dict[str, Any]:
|
|
57
|
-
"""Parse YAML frontmatter from skill markdown file.
|
|
166
|
+
"""Parse YAML frontmatter from skill markdown file with spec validation.
|
|
167
|
+
|
|
168
|
+
Supports both agentskills.io spec format and legacy claude-mpm format
|
|
169
|
+
with automatic migration.
|
|
58
170
|
|
|
59
171
|
Returns:
|
|
60
172
|
Dict with frontmatter fields or empty dict if no frontmatter
|
|
@@ -70,11 +182,150 @@ class SkillsRegistry:
|
|
|
70
182
|
|
|
71
183
|
try:
|
|
72
184
|
frontmatter = yaml.safe_load(match.group(1))
|
|
73
|
-
|
|
185
|
+
if not frontmatter:
|
|
186
|
+
return {}
|
|
187
|
+
|
|
188
|
+
# Apply backward compatibility migration
|
|
189
|
+
return self._apply_backward_compatibility(frontmatter)
|
|
74
190
|
except yaml.YAMLError as e:
|
|
75
191
|
logger.warning(f"Failed to parse skill frontmatter: {e}")
|
|
76
192
|
return {}
|
|
77
193
|
|
|
194
|
+
def _apply_backward_compatibility(
|
|
195
|
+
self, frontmatter: Dict[str, Any]
|
|
196
|
+
) -> Dict[str, Any]:
|
|
197
|
+
"""Apply backward compatibility transformations to legacy frontmatter.
|
|
198
|
+
|
|
199
|
+
Auto-migrates legacy claude-mpm fields to agentskills.io spec format:
|
|
200
|
+
- version → metadata.version
|
|
201
|
+
- author → metadata.author
|
|
202
|
+
- updated → metadata.updated
|
|
203
|
+
- tags → metadata.tags (if not already present)
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
frontmatter: Parsed frontmatter dict
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Transformed frontmatter with spec-compliant structure
|
|
210
|
+
"""
|
|
211
|
+
# Initialize metadata if not present
|
|
212
|
+
if "metadata" not in frontmatter:
|
|
213
|
+
frontmatter["metadata"] = {}
|
|
214
|
+
|
|
215
|
+
metadata = frontmatter["metadata"]
|
|
216
|
+
|
|
217
|
+
# Auto-migrate version (top-level → metadata.version)
|
|
218
|
+
if "version" in frontmatter and "version" not in metadata:
|
|
219
|
+
metadata["version"] = frontmatter["version"]
|
|
220
|
+
logger.debug(
|
|
221
|
+
f"Auto-migrated 'version' to metadata for skill '{frontmatter.get('name', 'unknown')}'"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Auto-migrate author (top-level → metadata.author)
|
|
225
|
+
if "author" in frontmatter and "author" not in metadata:
|
|
226
|
+
metadata["author"] = frontmatter["author"]
|
|
227
|
+
logger.debug(
|
|
228
|
+
f"Auto-migrated 'author' to metadata for skill '{frontmatter.get('name', 'unknown')}'"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Auto-migrate updated (top-level → metadata.updated)
|
|
232
|
+
if "updated" in frontmatter and "updated" not in metadata:
|
|
233
|
+
metadata["updated"] = frontmatter["updated"]
|
|
234
|
+
logger.debug(
|
|
235
|
+
f"Auto-migrated 'updated' to metadata for skill '{frontmatter.get('name', 'unknown')}'"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Auto-migrate tags (top-level → metadata.tags)
|
|
239
|
+
if "tags" in frontmatter and "tags" not in metadata:
|
|
240
|
+
metadata["tags"] = frontmatter["tags"]
|
|
241
|
+
logger.debug(
|
|
242
|
+
f"Auto-migrated 'tags' to metadata for skill '{frontmatter.get('name', 'unknown')}'"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Parse allowed-tools from space-delimited string to list
|
|
246
|
+
if "allowed-tools" in frontmatter:
|
|
247
|
+
allowed_tools = frontmatter["allowed-tools"]
|
|
248
|
+
if isinstance(allowed_tools, str):
|
|
249
|
+
frontmatter["allowed-tools"] = allowed_tools.split()
|
|
250
|
+
|
|
251
|
+
# Set default compatibility for claude-code if not present
|
|
252
|
+
if "compatibility" not in frontmatter:
|
|
253
|
+
frontmatter["compatibility"] = "claude-code"
|
|
254
|
+
|
|
255
|
+
return frontmatter
|
|
256
|
+
|
|
257
|
+
def _create_skill_from_frontmatter(
|
|
258
|
+
self, frontmatter: Dict[str, Any], path: Path, content: str, source: str
|
|
259
|
+
) -> Optional[Skill]:
|
|
260
|
+
"""Create Skill object from frontmatter with spec compliance.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
frontmatter: Parsed and migrated frontmatter dict
|
|
264
|
+
path: Path to skill file
|
|
265
|
+
content: Full skill content
|
|
266
|
+
source: Source type ('bundled', 'user', 'project', 'pm')
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Skill object or None if required fields missing
|
|
270
|
+
"""
|
|
271
|
+
# Extract spec fields (required)
|
|
272
|
+
name = frontmatter.get("name")
|
|
273
|
+
description = frontmatter.get("description", "")
|
|
274
|
+
|
|
275
|
+
# If name not in frontmatter, use filename stem
|
|
276
|
+
if not name:
|
|
277
|
+
name = path.stem
|
|
278
|
+
|
|
279
|
+
# If description not in frontmatter, extract from content
|
|
280
|
+
if not description:
|
|
281
|
+
description = self._extract_description(content)
|
|
282
|
+
|
|
283
|
+
# Validate required fields
|
|
284
|
+
if not name or not description:
|
|
285
|
+
logger.warning(
|
|
286
|
+
f"Skipping skill at {path}: missing required field (name or description)"
|
|
287
|
+
)
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
# Extract spec fields (optional)
|
|
291
|
+
license_field = frontmatter.get("license")
|
|
292
|
+
compatibility = frontmatter.get("compatibility", "claude-code")
|
|
293
|
+
metadata = frontmatter.get("metadata", {})
|
|
294
|
+
allowed_tools = frontmatter.get("allowed-tools", [])
|
|
295
|
+
|
|
296
|
+
# Extract derived fields from metadata or top-level
|
|
297
|
+
version = frontmatter.get("version") or metadata.get("version", "0.1.0")
|
|
298
|
+
skill_id = frontmatter.get("skill_id", name)
|
|
299
|
+
updated_at = frontmatter.get("updated_at") or metadata.get("updated")
|
|
300
|
+
tags = frontmatter.get("tags", []) or metadata.get("tags", [])
|
|
301
|
+
|
|
302
|
+
# Extract claude-mpm extensions
|
|
303
|
+
category = frontmatter.get("category")
|
|
304
|
+
toolchain = frontmatter.get("toolchain")
|
|
305
|
+
progressive_disclosure = frontmatter.get("progressive_disclosure")
|
|
306
|
+
user_invocable = frontmatter.get("user-invocable", False)
|
|
307
|
+
|
|
308
|
+
# Create skill object
|
|
309
|
+
return Skill(
|
|
310
|
+
name=name,
|
|
311
|
+
description=description,
|
|
312
|
+
license=license_field,
|
|
313
|
+
compatibility=compatibility,
|
|
314
|
+
metadata=metadata,
|
|
315
|
+
allowed_tools=allowed_tools,
|
|
316
|
+
path=path,
|
|
317
|
+
content=content,
|
|
318
|
+
source=source,
|
|
319
|
+
version=version,
|
|
320
|
+
skill_id=skill_id,
|
|
321
|
+
updated_at=updated_at,
|
|
322
|
+
tags=tags,
|
|
323
|
+
category=category,
|
|
324
|
+
toolchain=toolchain,
|
|
325
|
+
progressive_disclosure=progressive_disclosure,
|
|
326
|
+
user_invocable=user_invocable,
|
|
327
|
+
)
|
|
328
|
+
|
|
78
329
|
def _load_bundled_skills(self):
|
|
79
330
|
"""Load skills bundled with MPM."""
|
|
80
331
|
bundled_dir = Path(__file__).parent / "bundled"
|
|
@@ -88,32 +339,16 @@ class SkillsRegistry:
|
|
|
88
339
|
skill_name = skill_file.stem
|
|
89
340
|
content = skill_file.read_text(encoding="utf-8")
|
|
90
341
|
|
|
91
|
-
# Parse frontmatter
|
|
342
|
+
# Parse frontmatter with backward compatibility
|
|
92
343
|
frontmatter = self._parse_skill_frontmatter(content)
|
|
93
344
|
|
|
94
|
-
#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
updated_at = frontmatter.get("updated_at")
|
|
98
|
-
tags = frontmatter.get("tags", [])
|
|
99
|
-
|
|
100
|
-
# Extract description (from frontmatter or fallback to content parsing)
|
|
101
|
-
description = frontmatter.get("description", "")
|
|
102
|
-
if not description:
|
|
103
|
-
description = self._extract_description(content)
|
|
104
|
-
|
|
105
|
-
self.skills[skill_name] = Skill(
|
|
106
|
-
name=skill_name,
|
|
107
|
-
path=skill_file,
|
|
108
|
-
content=content,
|
|
109
|
-
source="bundled",
|
|
110
|
-
version=version,
|
|
111
|
-
skill_id=skill_id,
|
|
112
|
-
description=description,
|
|
113
|
-
updated_at=updated_at,
|
|
114
|
-
tags=tags,
|
|
345
|
+
# Create skill from frontmatter
|
|
346
|
+
skill = self._create_skill_from_frontmatter(
|
|
347
|
+
frontmatter, skill_file, content, "bundled"
|
|
115
348
|
)
|
|
116
|
-
|
|
349
|
+
if skill:
|
|
350
|
+
self.skills[skill_name] = skill
|
|
351
|
+
skill_count += 1
|
|
117
352
|
except Exception as e:
|
|
118
353
|
logger.error(f"Error loading bundled skill {skill_file}: {e}")
|
|
119
354
|
|
|
@@ -130,36 +365,20 @@ class SkillsRegistry:
|
|
|
130
365
|
for skill_file in user_skills_dir.glob("*.md"):
|
|
131
366
|
try:
|
|
132
367
|
skill_name = skill_file.stem
|
|
133
|
-
# User skills override bundled skills
|
|
134
368
|
content = skill_file.read_text(encoding="utf-8")
|
|
135
369
|
|
|
136
|
-
# Parse frontmatter
|
|
370
|
+
# Parse frontmatter with backward compatibility
|
|
137
371
|
frontmatter = self._parse_skill_frontmatter(content)
|
|
138
372
|
|
|
139
|
-
#
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
updated_at = frontmatter.get("updated_at")
|
|
143
|
-
tags = frontmatter.get("tags", [])
|
|
144
|
-
|
|
145
|
-
# Extract description (from frontmatter or fallback to content parsing)
|
|
146
|
-
description = frontmatter.get("description", "")
|
|
147
|
-
if not description:
|
|
148
|
-
description = self._extract_description(content)
|
|
149
|
-
|
|
150
|
-
self.skills[skill_name] = Skill(
|
|
151
|
-
name=skill_name,
|
|
152
|
-
path=skill_file,
|
|
153
|
-
content=content,
|
|
154
|
-
source="user",
|
|
155
|
-
version=version,
|
|
156
|
-
skill_id=skill_id,
|
|
157
|
-
description=description,
|
|
158
|
-
updated_at=updated_at,
|
|
159
|
-
tags=tags,
|
|
373
|
+
# Create skill from frontmatter
|
|
374
|
+
skill = self._create_skill_from_frontmatter(
|
|
375
|
+
frontmatter, skill_file, content, "user"
|
|
160
376
|
)
|
|
161
|
-
|
|
162
|
-
|
|
377
|
+
if skill:
|
|
378
|
+
# User skills override bundled skills
|
|
379
|
+
self.skills[skill_name] = skill
|
|
380
|
+
skill_count += 1
|
|
381
|
+
logger.debug(f"User skill '{skill_name}' overrides bundled version")
|
|
163
382
|
except Exception as e:
|
|
164
383
|
logger.error(f"Error loading user skill {skill_file}: {e}")
|
|
165
384
|
|
|
@@ -177,36 +396,22 @@ class SkillsRegistry:
|
|
|
177
396
|
for skill_file in project_skills_dir.glob("*.md"):
|
|
178
397
|
try:
|
|
179
398
|
skill_name = skill_file.stem
|
|
180
|
-
# Project skills override both user and bundled skills
|
|
181
399
|
content = skill_file.read_text(encoding="utf-8")
|
|
182
400
|
|
|
183
|
-
# Parse frontmatter
|
|
401
|
+
# Parse frontmatter with backward compatibility
|
|
184
402
|
frontmatter = self._parse_skill_frontmatter(content)
|
|
185
403
|
|
|
186
|
-
#
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
updated_at = frontmatter.get("updated_at")
|
|
190
|
-
tags = frontmatter.get("tags", [])
|
|
191
|
-
|
|
192
|
-
# Extract description (from frontmatter or fallback to content parsing)
|
|
193
|
-
description = frontmatter.get("description", "")
|
|
194
|
-
if not description:
|
|
195
|
-
description = self._extract_description(content)
|
|
196
|
-
|
|
197
|
-
self.skills[skill_name] = Skill(
|
|
198
|
-
name=skill_name,
|
|
199
|
-
path=skill_file,
|
|
200
|
-
content=content,
|
|
201
|
-
source="project",
|
|
202
|
-
version=version,
|
|
203
|
-
skill_id=skill_id,
|
|
204
|
-
description=description,
|
|
205
|
-
updated_at=updated_at,
|
|
206
|
-
tags=tags,
|
|
404
|
+
# Create skill from frontmatter
|
|
405
|
+
skill = self._create_skill_from_frontmatter(
|
|
406
|
+
frontmatter, skill_file, content, "project"
|
|
207
407
|
)
|
|
208
|
-
|
|
209
|
-
|
|
408
|
+
if skill:
|
|
409
|
+
# Project skills override both user and bundled skills
|
|
410
|
+
self.skills[skill_name] = skill
|
|
411
|
+
skill_count += 1
|
|
412
|
+
logger.debug(
|
|
413
|
+
f"Project skill '{skill_name}' overrides other versions"
|
|
414
|
+
)
|
|
210
415
|
except Exception as e:
|
|
211
416
|
logger.error(f"Error loading project skill {skill_file}: {e}")
|
|
212
417
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-mpm
|
|
3
|
-
Version: 5.6.
|
|
3
|
+
Version: 5.6.13
|
|
4
4
|
Summary: Claude Multi-Agent Project Manager - Orchestrate Claude with agent delegation and ticket tracking
|
|
5
5
|
Author-email: Bob Matsuoka <bob@matsuoka.com>
|
|
6
6
|
Maintainer: Claude MPM Team
|
|
@@ -143,9 +143,11 @@ Claude MPM transforms Claude Code into a **multi-agent orchestration platform**
|
|
|
143
143
|
|
|
144
144
|
### Prerequisites
|
|
145
145
|
|
|
146
|
-
1. **Python 3.
|
|
146
|
+
1. **Python 3.11+** (required - older versions will install outdated claude-mpm)
|
|
147
147
|
2. **Claude Code CLI v2.1.3+** (required!)
|
|
148
148
|
|
|
149
|
+
> ⚠️ **Python Version Note**: Claude MPM requires Python 3.11 or higher. If you have Python 3.9 or 3.10, you'll get an old version (4.x) that lacks current features. Check with `python3 --version` before installing.
|
|
150
|
+
|
|
149
151
|
```bash
|
|
150
152
|
# Verify Claude Code is installed
|
|
151
153
|
claude --version
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=9JfxhnDtr-8l3kCP2U5TVXSErptHoga8m7XA8zqgGOc,4
|
|
2
|
-
claude_mpm/VERSION,sha256=
|
|
2
|
+
claude_mpm/VERSION,sha256=ZrTpz3bolbYqLISSD7E_ue7APgTX0vbtj_G5SRWWxOI,7
|
|
3
3
|
claude_mpm/__init__.py,sha256=AGfh00BHKvLYD-UVFw7qbKtl7NMRIzRXOWw7vEuZ-h4,2214
|
|
4
4
|
claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
|
|
5
5
|
claude_mpm/constants.py,sha256=pz3lTrZZR5HhV3eZzYtIbtBwWo7iM6pkBHP_ixxmI6Y,6827
|
|
@@ -414,7 +414,7 @@ claude_mpm/hooks/claude_hooks/__init__.py,sha256=b4mud_g3S-3itHY_Dzpbb_SmdMEcJwt
|
|
|
414
414
|
claude_mpm/hooks/claude_hooks/auto_pause_handler.py,sha256=Iy80whkkPFJpn1z7u8PY57r9LC5BvEg3wfDlyknv0a0,17184
|
|
415
415
|
claude_mpm/hooks/claude_hooks/connection_pool.py,sha256=vpi-XbVf61GWhh85tHBzubbOgbJly_I-5-QmsleND2M,8658
|
|
416
416
|
claude_mpm/hooks/claude_hooks/correlation_manager.py,sha256=3n-RxzqE8egG4max_NcpJgL9gzrBY6Ti529LrjleI1g,2033
|
|
417
|
-
claude_mpm/hooks/claude_hooks/event_handlers.py,sha256=
|
|
417
|
+
claude_mpm/hooks/claude_hooks/event_handlers.py,sha256=ztkKTr5xFAY-K5gxhsXFtR_4tir3Cjx2l4auSYbJErU,46745
|
|
418
418
|
claude_mpm/hooks/claude_hooks/hook_handler.py,sha256=UOl5IVvz0Ro8Z0Owx4sKUWCxoIhvQpt7VTJ8lRC7Y8o,28266
|
|
419
419
|
claude_mpm/hooks/claude_hooks/hook_wrapper.sh,sha256=XYkdYtcM0nfnwYvMdyIFCasr80ry3uI5-fLYsLtDGw4,2214
|
|
420
420
|
claude_mpm/hooks/claude_hooks/installer.py,sha256=GgXqRjm_LpukrMl-eLw3cVDJqwwemNAaEKZvKM0QDYQ,34919
|
|
@@ -428,13 +428,13 @@ claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc,sha
|
|
|
428
428
|
claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc,sha256=aUV7xCwxrzM4iW4Jf0RbUskIs2whf7lXt6HppAEMp1g,18982
|
|
429
429
|
claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc,sha256=vo_mPXj0KTtBz2_SAU4NZ7TEWPfgBZtLeXD3CMAvJ3M,21381
|
|
430
430
|
claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc,sha256=SQX5iiP9bQZkLL-cj_2tlGH7lpAzarO0mYal7btj3tc,3521
|
|
431
|
-
claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc,sha256=
|
|
431
|
+
claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc,sha256=72KlsOUX_pQDIl36j7sQukPRarsLIsjINtzsBIpxCtw,45568
|
|
432
432
|
claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc,sha256=3ezkq2yC1cejVa-II4cl335SWCJhD8dv_NEDtVsCpUo,39235
|
|
433
|
-
claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc,sha256=
|
|
433
|
+
claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc,sha256=zoDVrDzFnxGtDHqH20seS7KhA-9rmtFqttD-sqINBQI,43516
|
|
434
434
|
claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc,sha256=vhJjvAMTdDWkB_qWaWr_PeDb4I8-h7UymR1wDVHeyYM,30789
|
|
435
435
|
claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc,sha256=bj168CD_Md4vpYJfS9YvPafP7-nw1hAUnuuEpzSQNKY,27815
|
|
436
436
|
claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc,sha256=RVz6WvW-sD7kLbh6S5a96WarpPvZv4_qfFMR5SN8WUo,30603
|
|
437
|
-
claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc,sha256=
|
|
437
|
+
claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc,sha256=tQ1esmuvKPb9PNp-a82X7W-5FGuDSWeQd72Xv4DJ18U,38443
|
|
438
438
|
claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc,sha256=Mlynkrh8uwlSOTCEH0LYbDM0gxqJmaCL9Gb5DIt_rJ4,38836
|
|
439
439
|
claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc,sha256=Xu_hwOf9AG0mZthl5gcDcVv68FiSo5KgZ6lDxdZXCfU,11185
|
|
440
440
|
claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc,sha256=w9SKmvg60p6kvNzuOIddYE-X9LYt-ZQzX3LWiKs4nZY,10055
|
|
@@ -454,6 +454,7 @@ claude_mpm/hooks/claude_hooks/services/subagent_processor.py,sha256=unHGeU6PJxc0
|
|
|
454
454
|
claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc,sha256=xBfLBSqnpcKfcQBWfh7xUm454g1lq1LvbO7SxGvcOPc,644
|
|
455
455
|
claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc,sha256=75pkmoqE7CeBOc2ZHTYfgSR-mtVjKrcKaOA6PxFGW64,565
|
|
456
456
|
claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc,sha256=nZDxNz67tIFwbZNRqyCRX8cSi0v3JKCZS5U5FhwwApA,562
|
|
457
|
+
claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc,sha256=IHMkHtVU8Noovuc3W9Eq0-GLPgTnGIL15RKnGD3X49o,10382
|
|
457
458
|
claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc,sha256=SydGEQ9oUWs161Ro4ULHfnPB0wiZgiB7V628B3V6N2U,9613
|
|
458
459
|
claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc,sha256=qdBU-mDyJxcwjwxfGhqzvo_s7YMO4mva-8hdObDAOcY,8616
|
|
459
460
|
claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc,sha256=PjdPDUyUQWm0FqYRdG2knPoZDSKS0g1vk-BDAXfzCi8,9627
|
|
@@ -886,9 +887,9 @@ claude_mpm/services/version_control/semantic_versioning.py,sha256=AUBteczJVCj0Ep
|
|
|
886
887
|
claude_mpm/services/version_control/version_parser.py,sha256=DbNncYZKKy9--ZUCO9gmqpfZn0jCTjJUPsnujlZ0Az4,19741
|
|
887
888
|
claude_mpm/services/visualization/__init__.py,sha256=tEnMAzCHmEgYV_cUa8AE0e69-S1CP94y9CR-3ceR6sM,384
|
|
888
889
|
claude_mpm/services/visualization/mermaid_generator.py,sha256=GIThvzs6BSH2oqtaT4hG8L7BfjdXpaCRKNpkOTgXcsM,34243
|
|
889
|
-
claude_mpm/skills/__init__.py,sha256=
|
|
890
|
+
claude_mpm/skills/__init__.py,sha256=VEJgYA9d1iqxqDfi6tb_a_qKbENqYlfwwet1kL8NzfY,1307
|
|
890
891
|
claude_mpm/skills/agent_skills_injector.py,sha256=djkKgm2LzaCVFoxv3XEyNr1NvtZu5Tabm-3Xvrf9e0Q,11839
|
|
891
|
-
claude_mpm/skills/registry.py,sha256=
|
|
892
|
+
claude_mpm/skills/registry.py,sha256=Vv-foCLhqTbem1E_75HgnTyHXebCqc-G7Q3aIrwxj9g,18334
|
|
892
893
|
claude_mpm/skills/skill_manager.py,sha256=5GwT-2BZWWg65hxA5Z7mL4JCBzVQBh7jswNYTtXjOF4,13979
|
|
893
894
|
claude_mpm/skills/skills_registry.py,sha256=Y06hde9S8PyrfiGYgscXK66qX5b0LGtoWJwnGMfKd4U,12331
|
|
894
895
|
claude_mpm/skills/skills_service.py,sha256=s_2eU-1zjDC7b4vsBSEupAtvWOqRON0BLl9Q-RtV6Ag,26530
|
|
@@ -1112,10 +1113,10 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
|
|
|
1112
1113
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
|
1113
1114
|
claude_mpm/validation/agent_validator.py,sha256=GprtAvu80VyMXcKGsK_VhYiXWA6BjKHv7O6HKx0AB9w,20917
|
|
1114
1115
|
claude_mpm/validation/frontmatter_validator.py,sha256=YpJlYNNYcV8u6hIOi3_jaRsDnzhbcQpjCBE6eyBKaFY,7076
|
|
1115
|
-
claude_mpm-5.6.
|
|
1116
|
-
claude_mpm-5.6.
|
|
1117
|
-
claude_mpm-5.6.
|
|
1118
|
-
claude_mpm-5.6.
|
|
1119
|
-
claude_mpm-5.6.
|
|
1120
|
-
claude_mpm-5.6.
|
|
1121
|
-
claude_mpm-5.6.
|
|
1116
|
+
claude_mpm-5.6.13.dist-info/licenses/LICENSE,sha256=ca3y_Rk4aPrbF6f62z8Ht5MJM9OAvbGlHvEDcj9vUQ4,3867
|
|
1117
|
+
claude_mpm-5.6.13.dist-info/licenses/LICENSE-FAQ.md,sha256=TxfEkXVCK98RzDOer09puc7JVCP_q_bN4dHtZKHCMcM,5104
|
|
1118
|
+
claude_mpm-5.6.13.dist-info/METADATA,sha256=sH-tdqfDZNpgGwO38ZlAaKnq9gntlJBWerfqfKi56q0,15246
|
|
1119
|
+
claude_mpm-5.6.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
1120
|
+
claude_mpm-5.6.13.dist-info/entry_points.txt,sha256=n-Uk4vwHPpuvu-g_I7-GHORzTnN_m6iyOsoLveKKD0E,228
|
|
1121
|
+
claude_mpm-5.6.13.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
|
1122
|
+
claude_mpm-5.6.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|