claude-mpm 4.15.3__py3-none-any.whl → 4.16.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/templates/agentic-coder-optimizer.json +9 -2
- claude_mpm/agents/templates/api_qa.json +7 -1
- claude_mpm/agents/templates/clerk-ops.json +8 -1
- claude_mpm/agents/templates/code_analyzer.json +4 -1
- claude_mpm/agents/templates/dart_engineer.json +11 -1
- claude_mpm/agents/templates/data_engineer.json +11 -1
- claude_mpm/agents/templates/documentation.json +6 -1
- claude_mpm/agents/templates/engineer.json +13 -0
- claude_mpm/agents/templates/gcp_ops_agent.json +8 -1
- claude_mpm/agents/templates/golang_engineer.json +11 -1
- claude_mpm/agents/templates/java_engineer.json +12 -2
- claude_mpm/agents/templates/local_ops_agent.json +216 -37
- claude_mpm/agents/templates/nextjs_engineer.json +11 -1
- claude_mpm/agents/templates/ops.json +8 -1
- claude_mpm/agents/templates/php-engineer.json +11 -1
- claude_mpm/agents/templates/project_organizer.json +9 -2
- claude_mpm/agents/templates/prompt-engineer.json +5 -1
- claude_mpm/agents/templates/python_engineer.json +11 -1
- claude_mpm/agents/templates/qa.json +7 -1
- claude_mpm/agents/templates/react_engineer.json +11 -1
- claude_mpm/agents/templates/refactoring_engineer.json +8 -1
- claude_mpm/agents/templates/research.json +4 -1
- claude_mpm/agents/templates/ruby-engineer.json +11 -1
- claude_mpm/agents/templates/rust_engineer.json +11 -1
- claude_mpm/agents/templates/security.json +6 -1
- claude_mpm/agents/templates/ticketing.json +6 -1
- claude_mpm/agents/templates/typescript_engineer.json +11 -1
- claude_mpm/agents/templates/vercel_ops_agent.json +8 -1
- claude_mpm/agents/templates/version_control.json +8 -1
- claude_mpm/agents/templates/web_qa.json +7 -1
- claude_mpm/agents/templates/web_ui.json +11 -1
- claude_mpm/cli/commands/configure.py +164 -16
- claude_mpm/cli/commands/configure_agent_display.py +6 -6
- claude_mpm/cli/commands/configure_behavior_manager.py +8 -8
- claude_mpm/cli/commands/configure_navigation.py +20 -18
- claude_mpm/cli/commands/configure_startup_manager.py +14 -14
- claude_mpm/cli/commands/configure_template_editor.py +8 -8
- claude_mpm/cli/interactive/__init__.py +3 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/startup.py +26 -0
- claude_mpm/dashboard/static/js/dashboard.js +0 -14
- claude_mpm/dashboard/templates/index.html +3 -41
- claude_mpm/services/socketio/handlers/__init__.py +5 -2
- claude_mpm/services/socketio/handlers/hook.py +10 -0
- claude_mpm/services/socketio/handlers/registry.py +4 -2
- claude_mpm/services/socketio/server/main.py +7 -7
- claude_mpm/skills/__init__.py +21 -0
- claude_mpm/skills/bundled/__init__.py +6 -0
- claude_mpm/skills/registry.py +198 -0
- claude_mpm/skills/skill_manager.py +310 -0
- {claude_mpm-4.15.3.dist-info → claude_mpm-4.16.0.dist-info}/METADATA +37 -8
- {claude_mpm-4.15.3.dist-info → claude_mpm-4.16.0.dist-info}/RECORD +57 -72
- claude_mpm/dashboard/static/css/code-tree.css +0 -1639
- claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
- claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
- claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
- claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
- claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
- claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
- {claude_mpm-4.15.3.dist-info → claude_mpm-4.16.0.dist-info}/WHEEL +0 -0
- {claude_mpm-4.15.3.dist-info → claude_mpm-4.16.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.15.3.dist-info → claude_mpm-4.16.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.15.3.dist-info → claude_mpm-4.16.0.dist-info}/top_level.txt +0 -0
|
@@ -61,6 +61,10 @@ class HookEventHandler(BaseEventHandler):
|
|
|
61
61
|
|
|
62
62
|
hook_data = data.get("data", {})
|
|
63
63
|
|
|
64
|
+
# Log hook event processing
|
|
65
|
+
tool_name = hook_data.get("tool_name", "N/A")
|
|
66
|
+
self.logger.info(f"Processing hook event: {hook_event} - tool: {tool_name}")
|
|
67
|
+
|
|
64
68
|
# Create properly formatted event for history
|
|
65
69
|
# Note: add_to_history expects the event data directly, not wrapped
|
|
66
70
|
history_event = {
|
|
@@ -77,6 +81,12 @@ class HookEventHandler(BaseEventHandler):
|
|
|
77
81
|
|
|
78
82
|
# Broadcast the original event to all connected clients
|
|
79
83
|
# (preserves all original fields)
|
|
84
|
+
connected_clients = (
|
|
85
|
+
len(self.server.clients) if hasattr(self.server, "clients") else 0
|
|
86
|
+
)
|
|
87
|
+
self.logger.info(
|
|
88
|
+
f"Broadcasting claude_event to {connected_clients} clients: {hook_event}"
|
|
89
|
+
)
|
|
80
90
|
await self.broadcast_event("claude_event", data)
|
|
81
91
|
|
|
82
92
|
# Track sessions based on hook events
|
|
@@ -15,7 +15,8 @@ if TYPE_CHECKING:
|
|
|
15
15
|
|
|
16
16
|
from ..server import SocketIOServer
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
# DISABLED: File Tree interface removed from dashboard
|
|
19
|
+
# from .code_analysis import CodeAnalysisEventHandler
|
|
19
20
|
from .connection import ConnectionEventHandler
|
|
20
21
|
from .file import FileEventHandler
|
|
21
22
|
from .git import GitEventHandler
|
|
@@ -38,7 +39,8 @@ class EventHandlerRegistry:
|
|
|
38
39
|
HookEventHandler, # Hook events for session tracking
|
|
39
40
|
GitEventHandler, # Git operations
|
|
40
41
|
FileEventHandler, # File operations
|
|
41
|
-
|
|
42
|
+
# DISABLED: File Tree interface removed from dashboard
|
|
43
|
+
# CodeAnalysisEventHandler, # Code analysis for dashboard
|
|
42
44
|
ProjectEventHandler, # Project management (future)
|
|
43
45
|
MemoryEventHandler, # Memory management (future)
|
|
44
46
|
]
|
|
@@ -267,15 +267,15 @@ class SocketIOServer(SocketIOServiceInterface):
|
|
|
267
267
|
except Exception as e:
|
|
268
268
|
self.logger.error(f"Error during EventBus teardown: {e}")
|
|
269
269
|
|
|
270
|
-
# Stop
|
|
270
|
+
# Stop event handlers
|
|
271
271
|
if self.event_registry:
|
|
272
|
-
from ..handlers import
|
|
273
|
-
|
|
274
|
-
# Stop analysis runner
|
|
275
|
-
analysis_handler = self.event_registry.get_handler(CodeAnalysisEventHandler)
|
|
276
|
-
if analysis_handler and hasattr(analysis_handler, "cleanup"):
|
|
277
|
-
analysis_handler.cleanup()
|
|
272
|
+
from ..handlers import ConnectionEventHandler
|
|
278
273
|
|
|
274
|
+
# DISABLED: File Tree interface removed from dashboard
|
|
275
|
+
# Stop analysis runner (code analysis handler is disabled)
|
|
276
|
+
# analysis_handler = self.event_registry.get_handler(CodeAnalysisEventHandler)
|
|
277
|
+
# if analysis_handler and hasattr(analysis_handler, "cleanup"):
|
|
278
|
+
# analysis_handler.cleanup()
|
|
279
279
|
# Stop health monitoring in connection handler
|
|
280
280
|
conn_handler = self.event_registry.get_handler(ConnectionEventHandler)
|
|
281
281
|
if conn_handler and hasattr(conn_handler, "stop_health_monitoring"):
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Claude MPM Skills Package
|
|
3
|
+
|
|
4
|
+
Skills system for sharing common capabilities across agents.
|
|
5
|
+
This reduces redundancy by extracting shared patterns into reusable skills.
|
|
6
|
+
|
|
7
|
+
Skills can be:
|
|
8
|
+
- Bundled with MPM (in skills/bundled/)
|
|
9
|
+
- User-installed (in ~/.claude/skills/)
|
|
10
|
+
- Project-specific (in .claude/skills/)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .registry import Skill, SkillsRegistry, get_registry
|
|
14
|
+
from .skill_manager import SkillManager
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"Skill",
|
|
18
|
+
"SkillManager",
|
|
19
|
+
"SkillsRegistry",
|
|
20
|
+
"get_registry",
|
|
21
|
+
]
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""Skills registry - manages bundled and discovered skills."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from claude_mpm.core.logging_utils import get_logger
|
|
8
|
+
|
|
9
|
+
logger = get_logger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class Skill:
|
|
14
|
+
"""Represents a skill that can be used by agents."""
|
|
15
|
+
|
|
16
|
+
name: str
|
|
17
|
+
path: Path
|
|
18
|
+
content: str
|
|
19
|
+
source: str # 'bundled', 'user', or 'project'
|
|
20
|
+
description: str = ""
|
|
21
|
+
agent_types: List[str] = None # Which agent types can use this skill
|
|
22
|
+
|
|
23
|
+
def __post_init__(self):
|
|
24
|
+
"""Initialize agent_types list if not provided."""
|
|
25
|
+
if self.agent_types is None:
|
|
26
|
+
self.agent_types = []
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SkillsRegistry:
|
|
30
|
+
"""Registry for managing skills across all tiers."""
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
"""Initialize the skills registry."""
|
|
34
|
+
self.skills: Dict[str, Skill] = {}
|
|
35
|
+
self._load_bundled_skills()
|
|
36
|
+
self._load_user_skills()
|
|
37
|
+
self._load_project_skills()
|
|
38
|
+
|
|
39
|
+
def _load_bundled_skills(self):
|
|
40
|
+
"""Load skills bundled with MPM."""
|
|
41
|
+
bundled_dir = Path(__file__).parent / "bundled"
|
|
42
|
+
if not bundled_dir.exists():
|
|
43
|
+
logger.warning(f"Bundled skills directory not found: {bundled_dir}")
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
skill_count = 0
|
|
47
|
+
for skill_file in bundled_dir.glob("*.md"):
|
|
48
|
+
try:
|
|
49
|
+
skill_name = skill_file.stem
|
|
50
|
+
content = skill_file.read_text(encoding="utf-8")
|
|
51
|
+
|
|
52
|
+
# Extract description from first paragraph if available
|
|
53
|
+
description = self._extract_description(content)
|
|
54
|
+
|
|
55
|
+
self.skills[skill_name] = Skill(
|
|
56
|
+
name=skill_name,
|
|
57
|
+
path=skill_file,
|
|
58
|
+
content=content,
|
|
59
|
+
source="bundled",
|
|
60
|
+
description=description,
|
|
61
|
+
)
|
|
62
|
+
skill_count += 1
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.error(f"Error loading bundled skill {skill_file}: {e}")
|
|
65
|
+
|
|
66
|
+
logger.info(f"Loaded {skill_count} bundled skills")
|
|
67
|
+
|
|
68
|
+
def _load_user_skills(self):
|
|
69
|
+
"""Load user-installed skills from ~/.claude/skills/"""
|
|
70
|
+
user_skills_dir = Path.home() / ".claude" / "skills"
|
|
71
|
+
if not user_skills_dir.exists():
|
|
72
|
+
logger.debug("User skills directory not found, skipping")
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
skill_count = 0
|
|
76
|
+
for skill_file in user_skills_dir.glob("*.md"):
|
|
77
|
+
try:
|
|
78
|
+
skill_name = skill_file.stem
|
|
79
|
+
# User skills override bundled skills
|
|
80
|
+
content = skill_file.read_text(encoding="utf-8")
|
|
81
|
+
description = self._extract_description(content)
|
|
82
|
+
|
|
83
|
+
self.skills[skill_name] = Skill(
|
|
84
|
+
name=skill_name,
|
|
85
|
+
path=skill_file,
|
|
86
|
+
content=content,
|
|
87
|
+
source="user",
|
|
88
|
+
description=description,
|
|
89
|
+
)
|
|
90
|
+
skill_count += 1
|
|
91
|
+
logger.debug(f"User skill '{skill_name}' overrides bundled version")
|
|
92
|
+
except Exception as e:
|
|
93
|
+
logger.error(f"Error loading user skill {skill_file}: {e}")
|
|
94
|
+
|
|
95
|
+
if skill_count > 0:
|
|
96
|
+
logger.info(f"Loaded {skill_count} user skills")
|
|
97
|
+
|
|
98
|
+
def _load_project_skills(self):
|
|
99
|
+
"""Load project-specific skills from .claude/skills/"""
|
|
100
|
+
project_skills_dir = Path.cwd() / ".claude" / "skills"
|
|
101
|
+
if not project_skills_dir.exists():
|
|
102
|
+
logger.debug("Project skills directory not found, skipping")
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
skill_count = 0
|
|
106
|
+
for skill_file in project_skills_dir.glob("*.md"):
|
|
107
|
+
try:
|
|
108
|
+
skill_name = skill_file.stem
|
|
109
|
+
# Project skills override both user and bundled skills
|
|
110
|
+
content = skill_file.read_text(encoding="utf-8")
|
|
111
|
+
description = self._extract_description(content)
|
|
112
|
+
|
|
113
|
+
self.skills[skill_name] = Skill(
|
|
114
|
+
name=skill_name,
|
|
115
|
+
path=skill_file,
|
|
116
|
+
content=content,
|
|
117
|
+
source="project",
|
|
118
|
+
description=description,
|
|
119
|
+
)
|
|
120
|
+
skill_count += 1
|
|
121
|
+
logger.debug(f"Project skill '{skill_name}' overrides other versions")
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logger.error(f"Error loading project skill {skill_file}: {e}")
|
|
124
|
+
|
|
125
|
+
if skill_count > 0:
|
|
126
|
+
logger.info(f"Loaded {skill_count} project skills")
|
|
127
|
+
|
|
128
|
+
def _extract_description(self, content: str) -> str:
|
|
129
|
+
"""Extract description from skill content (first paragraph or summary)."""
|
|
130
|
+
lines = content.strip().split("\n")
|
|
131
|
+
description_lines = []
|
|
132
|
+
|
|
133
|
+
# Skip title (first line starting with #)
|
|
134
|
+
start_idx = 0
|
|
135
|
+
if lines and lines[0].startswith("#"):
|
|
136
|
+
start_idx = 1
|
|
137
|
+
|
|
138
|
+
# Find first non-empty paragraph
|
|
139
|
+
for line in lines[start_idx:]:
|
|
140
|
+
line = line.strip()
|
|
141
|
+
if not line:
|
|
142
|
+
if description_lines:
|
|
143
|
+
break
|
|
144
|
+
continue
|
|
145
|
+
if line.startswith("#"):
|
|
146
|
+
break
|
|
147
|
+
description_lines.append(line)
|
|
148
|
+
|
|
149
|
+
return " ".join(description_lines)[:200] # Limit to 200 chars
|
|
150
|
+
|
|
151
|
+
def get_skill(self, name: str) -> Optional[Skill]:
|
|
152
|
+
"""Get a skill by name."""
|
|
153
|
+
return self.skills.get(name)
|
|
154
|
+
|
|
155
|
+
def list_skills(self, source: Optional[str] = None) -> List[Skill]:
|
|
156
|
+
"""List all skills, optionally filtered by source."""
|
|
157
|
+
if source:
|
|
158
|
+
return [s for s in self.skills.values() if s.source == source]
|
|
159
|
+
return list(self.skills.values())
|
|
160
|
+
|
|
161
|
+
def get_skills_for_agent(self, agent_type: str) -> List[Skill]:
|
|
162
|
+
"""
|
|
163
|
+
Get skills mapped to a specific agent type.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
agent_type: Agent type/ID (e.g., 'engineer', 'python_engineer')
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
List of skills applicable to this agent type
|
|
170
|
+
"""
|
|
171
|
+
# Filter skills that explicitly list this agent type
|
|
172
|
+
# If a skill has no agent_types specified, it's available to all agents
|
|
173
|
+
return [
|
|
174
|
+
skill
|
|
175
|
+
for skill in self.skills.values()
|
|
176
|
+
if not skill.agent_types or agent_type in skill.agent_types
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
def reload(self):
|
|
180
|
+
"""Reload all skills from disk."""
|
|
181
|
+
logger.info("Reloading skills registry...")
|
|
182
|
+
self.skills.clear()
|
|
183
|
+
self._load_bundled_skills()
|
|
184
|
+
self._load_user_skills()
|
|
185
|
+
self._load_project_skills()
|
|
186
|
+
logger.info(f"Skills registry reloaded with {len(self.skills)} skills")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# Global registry instance (singleton pattern)
|
|
190
|
+
_registry: Optional[SkillsRegistry] = None
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def get_registry() -> SkillsRegistry:
|
|
194
|
+
"""Get the global skills registry (singleton)."""
|
|
195
|
+
global _registry
|
|
196
|
+
if _registry is None:
|
|
197
|
+
_registry = SkillsRegistry()
|
|
198
|
+
return _registry
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"""Skills manager - integrates skills with agents."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from claude_mpm.core.logging_utils import get_logger
|
|
8
|
+
|
|
9
|
+
from .registry import Skill, get_registry
|
|
10
|
+
|
|
11
|
+
logger = get_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SkillManager:
|
|
15
|
+
"""Manages skills and their integration with agents."""
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
"""Initialize the skill manager."""
|
|
19
|
+
self.registry = get_registry()
|
|
20
|
+
self.agent_skill_mapping: Dict[str, List[str]] = {}
|
|
21
|
+
self._load_agent_mappings()
|
|
22
|
+
|
|
23
|
+
def _load_agent_mappings(self):
|
|
24
|
+
"""Load skill mappings from agent templates."""
|
|
25
|
+
# Load mappings from agent JSON templates that have 'skills' field
|
|
26
|
+
agent_templates_dir = Path(__file__).parent.parent / "agents" / "templates"
|
|
27
|
+
|
|
28
|
+
if not agent_templates_dir.exists():
|
|
29
|
+
logger.warning(
|
|
30
|
+
f"Agent templates directory not found: {agent_templates_dir}"
|
|
31
|
+
)
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
mapping_count = 0
|
|
35
|
+
for template_file in agent_templates_dir.glob("*.json"):
|
|
36
|
+
try:
|
|
37
|
+
with open(template_file, encoding="utf-8") as f:
|
|
38
|
+
agent_data = json.load(f)
|
|
39
|
+
|
|
40
|
+
agent_id = agent_data.get("agent_id") or agent_data.get("agent_type")
|
|
41
|
+
if not agent_id:
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
# Extract skills list if present
|
|
45
|
+
skills = agent_data.get("skills", [])
|
|
46
|
+
if skills:
|
|
47
|
+
self.agent_skill_mapping[agent_id] = skills
|
|
48
|
+
mapping_count += 1
|
|
49
|
+
logger.debug(
|
|
50
|
+
f"Agent '{agent_id}' mapped to {len(skills)} skills: {', '.join(skills)}"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
except Exception as e:
|
|
54
|
+
logger.error(f"Error loading agent mapping from {template_file}: {e}")
|
|
55
|
+
|
|
56
|
+
if mapping_count > 0:
|
|
57
|
+
logger.info(f"Loaded skill mappings for {mapping_count} agents")
|
|
58
|
+
|
|
59
|
+
def get_agent_skills(self, agent_type: str) -> List[Skill]:
|
|
60
|
+
"""
|
|
61
|
+
Get all skills for an agent (bundled + discovered).
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
agent_type: Agent type/ID (e.g., 'engineer', 'python_engineer')
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
List of Skill objects for this agent
|
|
68
|
+
"""
|
|
69
|
+
skill_names = self.agent_skill_mapping.get(agent_type, [])
|
|
70
|
+
|
|
71
|
+
# Get skills from registry
|
|
72
|
+
skills = []
|
|
73
|
+
for name in skill_names:
|
|
74
|
+
skill = self.registry.get_skill(name)
|
|
75
|
+
if skill:
|
|
76
|
+
skills.append(skill)
|
|
77
|
+
else:
|
|
78
|
+
logger.warning(
|
|
79
|
+
f"Skill '{name}' referenced by agent '{agent_type}' not found"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Also include skills that have no agent restriction
|
|
83
|
+
# or explicitly list this agent type
|
|
84
|
+
additional_skills = self.registry.get_skills_for_agent(agent_type)
|
|
85
|
+
for skill in additional_skills:
|
|
86
|
+
if skill not in skills:
|
|
87
|
+
skills.append(skill)
|
|
88
|
+
|
|
89
|
+
return skills
|
|
90
|
+
|
|
91
|
+
def enhance_agent_prompt(
|
|
92
|
+
self, agent_type: str, base_prompt: str, include_all: bool = False
|
|
93
|
+
) -> str:
|
|
94
|
+
"""
|
|
95
|
+
Enhance agent prompt with available skills.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
agent_type: Agent type/ID
|
|
99
|
+
base_prompt: Original agent prompt
|
|
100
|
+
include_all: If True, include all available skills regardless of mapping
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Enhanced prompt with skills section appended
|
|
104
|
+
"""
|
|
105
|
+
if include_all:
|
|
106
|
+
skills = self.registry.list_skills()
|
|
107
|
+
else:
|
|
108
|
+
skills = self.get_agent_skills(agent_type)
|
|
109
|
+
|
|
110
|
+
if not skills:
|
|
111
|
+
return base_prompt
|
|
112
|
+
|
|
113
|
+
# Build skills section
|
|
114
|
+
skills_section = "\n\n" + "=" * 80 + "\n"
|
|
115
|
+
skills_section += "## 🎯 Available Skills\n\n"
|
|
116
|
+
skills_section += f"You have access to {len(skills)} specialized skills:\n\n"
|
|
117
|
+
|
|
118
|
+
for skill in skills:
|
|
119
|
+
skills_section += f"### 📚 {skill.name.replace('-', ' ').title()}\n\n"
|
|
120
|
+
skills_section += f"**Source:** {skill.source}\n"
|
|
121
|
+
if skill.description:
|
|
122
|
+
skills_section += f"**Description:** {skill.description}\n"
|
|
123
|
+
skills_section += "\n```\n"
|
|
124
|
+
skills_section += skill.content
|
|
125
|
+
skills_section += "\n```\n\n"
|
|
126
|
+
|
|
127
|
+
skills_section += "=" * 80 + "\n"
|
|
128
|
+
|
|
129
|
+
return base_prompt + skills_section
|
|
130
|
+
|
|
131
|
+
def list_agent_skill_mappings(self) -> Dict[str, List[str]]:
|
|
132
|
+
"""
|
|
133
|
+
Get all agent-to-skill mappings.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Dictionary mapping agent IDs to lists of skill names
|
|
137
|
+
"""
|
|
138
|
+
return self.agent_skill_mapping.copy()
|
|
139
|
+
|
|
140
|
+
def add_skill_to_agent(self, agent_type: str, skill_name: str) -> bool:
|
|
141
|
+
"""
|
|
142
|
+
Add a skill to an agent's mapping.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
agent_type: Agent type/ID
|
|
146
|
+
skill_name: Name of the skill to add
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
True if successful, False if skill not found
|
|
150
|
+
"""
|
|
151
|
+
skill = self.registry.get_skill(skill_name)
|
|
152
|
+
if not skill:
|
|
153
|
+
logger.error(f"Cannot add skill '{skill_name}': skill not found")
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
if agent_type not in self.agent_skill_mapping:
|
|
157
|
+
self.agent_skill_mapping[agent_type] = []
|
|
158
|
+
|
|
159
|
+
if skill_name not in self.agent_skill_mapping[agent_type]:
|
|
160
|
+
self.agent_skill_mapping[agent_type].append(skill_name)
|
|
161
|
+
logger.info(f"Added skill '{skill_name}' to agent '{agent_type}'")
|
|
162
|
+
|
|
163
|
+
return True
|
|
164
|
+
|
|
165
|
+
def remove_skill_from_agent(self, agent_type: str, skill_name: str) -> bool:
|
|
166
|
+
"""
|
|
167
|
+
Remove a skill from an agent's mapping.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
agent_type: Agent type/ID
|
|
171
|
+
skill_name: Name of the skill to remove
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
True if successful, False if not found
|
|
175
|
+
"""
|
|
176
|
+
if agent_type not in self.agent_skill_mapping:
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
if skill_name in self.agent_skill_mapping[agent_type]:
|
|
180
|
+
self.agent_skill_mapping[agent_type].remove(skill_name)
|
|
181
|
+
logger.info(f"Removed skill '{skill_name}' from agent '{agent_type}'")
|
|
182
|
+
return True
|
|
183
|
+
|
|
184
|
+
return False
|
|
185
|
+
|
|
186
|
+
def reload(self):
|
|
187
|
+
"""Reload skills and agent mappings."""
|
|
188
|
+
logger.info("Reloading skill manager...")
|
|
189
|
+
self.registry.reload()
|
|
190
|
+
self.agent_skill_mapping.clear()
|
|
191
|
+
self._load_agent_mappings()
|
|
192
|
+
logger.info("Skill manager reloaded")
|
|
193
|
+
|
|
194
|
+
def infer_agents_for_skill(self, skill) -> List[str]:
|
|
195
|
+
"""Infer which agents should have this skill based on tags/name.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
skill: Skill object to analyze
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
List of agent IDs that should have this skill
|
|
202
|
+
"""
|
|
203
|
+
agents = []
|
|
204
|
+
content_lower = skill.content.lower()
|
|
205
|
+
name_lower = skill.name.lower()
|
|
206
|
+
|
|
207
|
+
# Python-related
|
|
208
|
+
if any(
|
|
209
|
+
tag in content_lower or tag in name_lower
|
|
210
|
+
for tag in ["python", "django", "flask", "fastapi"]
|
|
211
|
+
):
|
|
212
|
+
agents.append("python-engineer")
|
|
213
|
+
|
|
214
|
+
# TypeScript/JavaScript-related
|
|
215
|
+
if any(
|
|
216
|
+
tag in content_lower or tag in name_lower
|
|
217
|
+
for tag in ["typescript", "javascript", "react", "next", "vue", "node"]
|
|
218
|
+
):
|
|
219
|
+
agents.extend(["typescript-engineer", "react-engineer", "nextjs-engineer"])
|
|
220
|
+
|
|
221
|
+
# Go-related
|
|
222
|
+
if any(tag in content_lower or tag in name_lower for tag in ["golang", "go "]):
|
|
223
|
+
agents.append("golang-engineer")
|
|
224
|
+
|
|
225
|
+
# Ops-related
|
|
226
|
+
if any(
|
|
227
|
+
tag in content_lower or tag in name_lower
|
|
228
|
+
for tag in ["docker", "kubernetes", "deploy", "devops", "ops"]
|
|
229
|
+
):
|
|
230
|
+
agents.extend(["ops", "devops", "local-ops"])
|
|
231
|
+
|
|
232
|
+
# Testing/QA-related
|
|
233
|
+
if any(
|
|
234
|
+
tag in content_lower or tag in name_lower
|
|
235
|
+
for tag in ["test", "qa", "quality", "assert"]
|
|
236
|
+
):
|
|
237
|
+
agents.extend(["qa", "web-qa", "api-qa"])
|
|
238
|
+
|
|
239
|
+
# Documentation-related
|
|
240
|
+
if any(
|
|
241
|
+
tag in content_lower or tag in name_lower
|
|
242
|
+
for tag in ["documentation", "docs", "api doc", "openapi"]
|
|
243
|
+
):
|
|
244
|
+
agents.extend(["docs", "documentation", "technical-writer"])
|
|
245
|
+
|
|
246
|
+
# Remove duplicates
|
|
247
|
+
return list(set(agents))
|
|
248
|
+
|
|
249
|
+
def save_mappings_to_config(self, config_path: Optional[Path] = None):
|
|
250
|
+
"""Save current agent-skill mappings to configuration file.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
config_path: Path to configuration file. If None, uses default.
|
|
254
|
+
"""
|
|
255
|
+
import json
|
|
256
|
+
|
|
257
|
+
if config_path is None:
|
|
258
|
+
config_path = Path.cwd() / ".claude-mpm" / "skills_config.json"
|
|
259
|
+
|
|
260
|
+
# Ensure directory exists
|
|
261
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
262
|
+
|
|
263
|
+
# Save mappings
|
|
264
|
+
with open(config_path, "w", encoding="utf-8") as f:
|
|
265
|
+
json.dump(self.agent_skill_mapping, f, indent=2)
|
|
266
|
+
|
|
267
|
+
logger.info(f"Saved skill mappings to {config_path}")
|
|
268
|
+
|
|
269
|
+
def load_mappings_from_config(self, config_path: Optional[Path] = None):
|
|
270
|
+
"""Load agent-skill mappings from configuration file.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
config_path: Path to configuration file. If None, uses default.
|
|
274
|
+
"""
|
|
275
|
+
import json
|
|
276
|
+
|
|
277
|
+
if config_path is None:
|
|
278
|
+
config_path = Path.cwd() / ".claude-mpm" / "skills_config.json"
|
|
279
|
+
|
|
280
|
+
if not config_path.exists():
|
|
281
|
+
logger.debug(f"No skill mappings config found at {config_path}")
|
|
282
|
+
return
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
with open(config_path, encoding="utf-8") as f:
|
|
286
|
+
loaded_mappings = json.load(f)
|
|
287
|
+
|
|
288
|
+
# Merge with existing mappings
|
|
289
|
+
for agent_id, skills in loaded_mappings.items():
|
|
290
|
+
if agent_id not in self.agent_skill_mapping:
|
|
291
|
+
self.agent_skill_mapping[agent_id] = []
|
|
292
|
+
for skill in skills:
|
|
293
|
+
if skill not in self.agent_skill_mapping[agent_id]:
|
|
294
|
+
self.agent_skill_mapping[agent_id].append(skill)
|
|
295
|
+
|
|
296
|
+
logger.info(f"Loaded skill mappings from {config_path}")
|
|
297
|
+
except Exception as e:
|
|
298
|
+
logger.error(f"Error loading skill mappings from {config_path}: {e}")
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# Global manager instance (singleton pattern)
|
|
302
|
+
_manager: Optional[SkillManager] = None
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def get_manager() -> SkillManager:
|
|
306
|
+
"""Get the global skill manager (singleton)."""
|
|
307
|
+
global _manager
|
|
308
|
+
if _manager is None:
|
|
309
|
+
_manager = SkillManager()
|
|
310
|
+
return _manager
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-mpm
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.16.0
|
|
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
|
|
@@ -157,6 +157,7 @@ A powerful orchestration framework for **Claude Code (CLI)** that enables multi-
|
|
|
157
157
|
## Features
|
|
158
158
|
|
|
159
159
|
- 🤖 **Multi-Agent System**: 15 specialized agents for comprehensive project management
|
|
160
|
+
- 🎯 **Skills System**: 15 bundled skills with auto-linking, three-tier organization (bundled/user/project), and interactive configuration
|
|
160
161
|
- 🧠 **Persistent Knowledge System**: Project-specific kuzu-memory integration for intelligent context retention
|
|
161
162
|
- 🔄 **Session Management**: Resume previous sessions with `--resume`
|
|
162
163
|
- 📊 **Real-Time Monitoring**: Live dashboard with `--monitor` flag
|
|
@@ -305,6 +306,34 @@ Claude MPM includes 15 specialized agents:
|
|
|
305
306
|
### Agent Memory System
|
|
306
307
|
Agents learn project-specific patterns using a simple list format and can update memories via JSON response fields (`remember` for incremental updates, `MEMORIES` for complete replacement). Initialize with `claude-mpm memory init`.
|
|
307
308
|
|
|
309
|
+
### Skills System
|
|
310
|
+
|
|
311
|
+
Claude MPM includes a powerful skills system that eliminates redundant agent guidance through reusable skill modules:
|
|
312
|
+
|
|
313
|
+
**15 Bundled Skills** covering essential development workflows:
|
|
314
|
+
- Git workflow, TDD, code review, systematic debugging
|
|
315
|
+
- API documentation, refactoring patterns, performance profiling
|
|
316
|
+
- Docker containerization, database migrations, security scanning
|
|
317
|
+
- JSON/PDF/XLSX handling, async testing, ImageMagick operations
|
|
318
|
+
|
|
319
|
+
**Three-Tier Organization:**
|
|
320
|
+
- **Bundled**: Core skills included with Claude MPM (~4,700 lines of reusable guidance)
|
|
321
|
+
- **User**: Custom skills in `~/.config/claude-mpm/skills/`
|
|
322
|
+
- **Project**: Project-specific skills in `.claude-mpm/skills/`
|
|
323
|
+
|
|
324
|
+
**Quick Access:**
|
|
325
|
+
```bash
|
|
326
|
+
# Interactive skills management
|
|
327
|
+
claude-mpm configure
|
|
328
|
+
# Choose option 2: Skills Management
|
|
329
|
+
|
|
330
|
+
# Auto-link skills to agents based on their roles
|
|
331
|
+
# Configure custom skill assignments
|
|
332
|
+
# View current skill mappings
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Skills are automatically injected into agent prompts, reducing template size by 85% while maintaining full capability coverage.
|
|
336
|
+
|
|
308
337
|
### MCP Gateway (Model Context Protocol)
|
|
309
338
|
|
|
310
339
|
Claude MPM includes a powerful MCP Gateway that enables:
|
|
@@ -365,19 +394,19 @@ See [docs/MEMORY.md](docs/MEMORY.md) and [docs/developer/11-dashboard/README.md]
|
|
|
365
394
|
- **Single Entry Point**: [docs/README.md](docs/README.md) is your navigation hub
|
|
366
395
|
- **Clear User Paths**: Organized by user type and experience level
|
|
367
396
|
- **Cross-Referenced**: Links between related topics and sections
|
|
368
|
-
- **Up-to-Date**: Version 4.
|
|
397
|
+
- **Up-to-Date**: Version 4.16.0 with skills system and enhanced documentation
|
|
369
398
|
|
|
370
|
-
## Recent Updates (v4.
|
|
399
|
+
## Recent Updates (v4.16.0)
|
|
371
400
|
|
|
372
|
-
**
|
|
401
|
+
**Skills System Integration**: 15 bundled skills with auto-linking, three-tier organization, and interactive configuration. Eliminates 85% of redundant guidance across agent templates (~4,700 lines of reusable content).
|
|
373
402
|
|
|
374
|
-
**
|
|
403
|
+
**Enhanced Documentation**: Complete documentation suite with PDF guides, reorganized structure, and comprehensive design documents for skills integration.
|
|
375
404
|
|
|
376
|
-
**
|
|
405
|
+
**Agent Template Improvements**: Cleaned agent templates with skills integration, removing redundant guidance while maintaining full capability coverage.
|
|
377
406
|
|
|
378
|
-
**
|
|
407
|
+
**Interactive Skills Management**: New skills wizard accessible via `claude-mpm configure` for viewing, configuring, and auto-linking skills to agents.
|
|
379
408
|
|
|
380
|
-
**
|
|
409
|
+
**Bug Fixes**: Resolved agent template inconsistencies and improved configuration management.
|
|
381
410
|
|
|
382
411
|
See [CHANGELOG.md](CHANGELOG.md) for full history and [docs/user/MIGRATION.md](docs/user/MIGRATION.md) for upgrade instructions.
|
|
383
412
|
|