claude-mpm 5.4.21__py3-none-any.whl → 5.4.59__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/BASE_AGENT.md +164 -0
- claude_mpm/agents/BASE_ENGINEER.md +658 -0
- claude_mpm/agents/MEMORY.md +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +771 -1019
- claude_mpm/agents/WORKFLOW.md +5 -254
- claude_mpm/agents/agent_loader.py +1 -1
- claude_mpm/agents/base_agent.json +31 -0
- claude_mpm/agents/frontmatter_validator.py +2 -2
- claude_mpm/cli/commands/agent_state_manager.py +10 -10
- claude_mpm/cli/commands/agents.py +9 -9
- claude_mpm/cli/commands/auto_configure.py +4 -4
- claude_mpm/cli/commands/configure.py +1 -1
- claude_mpm/cli/commands/configure_agent_display.py +12 -0
- claude_mpm/cli/commands/mpm_init/core.py +72 -0
- claude_mpm/cli/commands/postmortem.py +1 -1
- claude_mpm/cli/commands/profile.py +276 -0
- claude_mpm/cli/commands/skills.py +14 -18
- claude_mpm/cli/executor.py +10 -0
- claude_mpm/cli/interactive/agent_wizard.py +2 -2
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/parsers/profile_parser.py +147 -0
- claude_mpm/cli/parsers/skills_parser.py +0 -6
- claude_mpm/cli/startup.py +506 -180
- claude_mpm/commands/mpm-config.md +13 -250
- claude_mpm/commands/mpm-doctor.md +9 -22
- claude_mpm/commands/mpm-help.md +5 -206
- claude_mpm/commands/mpm-init.md +81 -507
- claude_mpm/commands/mpm-monitor.md +15 -402
- claude_mpm/commands/mpm-organize.md +61 -441
- claude_mpm/commands/mpm-postmortem.md +6 -108
- claude_mpm/commands/mpm-session-resume.md +12 -363
- claude_mpm/commands/mpm-status.md +5 -69
- claude_mpm/commands/mpm-ticket-view.md +52 -495
- claude_mpm/commands/mpm-version.md +5 -107
- claude_mpm/core/config.py +2 -4
- claude_mpm/core/framework/loaders/agent_loader.py +1 -1
- claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
- claude_mpm/core/optimized_startup.py +61 -0
- claude_mpm/core/shared/config_loader.py +3 -1
- claude_mpm/core/unified_agent_registry.py +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B0uc0UOD.js +36 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7RN905-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7xVLGWV.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BIF9m_hv.js +61 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BPYeabCQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BSNlmTZj.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Be7GpZd6.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bh0LDWpI.js +145 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BofRWZRR.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BovzEFCE.js +30 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C30mlcqg.js +165 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4B-KCzX.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4JcI4KD.js +122 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CBBdVcY8.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CDuw-vjf.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C_Usid8X.js +15 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cfqx1Qun.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CiIAseT4.js +128 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CnA0NrzZ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cs_tUR18.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CyWMqx4W.js +43 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzZX-COe.js +220 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzeYkLYB.js +65 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D3k0OPJN.js +4 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9lljYKQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DGkLK5U1.js +267 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DI7hHRFL.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DLVjFsZ3.js +139 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUrLdbGD.js +89 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DY1XQ8fi.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DZX00Y4g.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DaimHw_p.js +68 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +323 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dhb8PKl3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dle-35c7.js +64 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DmxopI1J.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DwBR2MJi.js +60 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/GYwsonyD.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/NqQ1dWOy.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/RJiighC3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Vzk33B_K.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ZGh7QtNv.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bT1r9zLR.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bTOqqlTd.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/eNVUfhuA.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/iEWssX7S.js +162 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/sQeU3Y1z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uuIeMWc-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.D6-I5TpK.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.m1gL8KXf.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.CgNOuw-d.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
- claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
- claude_mpm/dashboard/static/svelte-build/index.html +36 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +149 -1
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +26 -6
- claude_mpm/hooks/kuzu_memory_hook.py +5 -5
- claude_mpm/init.py +276 -0
- claude_mpm/models/git_repository.py +3 -3
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/agent_builder.py +3 -3
- claude_mpm/services/agents/cache_git_manager.py +6 -6
- claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
- claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -2
- claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
- claude_mpm/services/agents/deployment/agent_template_builder.py +31 -19
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
- claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
- claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +169 -26
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +98 -75
- claude_mpm/services/agents/git_source_manager.py +23 -4
- claude_mpm/services/agents/recommender.py +5 -3
- claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
- claude_mpm/services/agents/sources/git_source_sync_service.py +121 -10
- claude_mpm/services/agents/startup_sync.py +22 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
- claude_mpm/services/git/git_operations_service.py +8 -8
- claude_mpm/services/monitor/management/lifecycle.py +7 -1
- claude_mpm/services/monitor/server.py +473 -3
- claude_mpm/services/pm_skills_deployer.py +711 -0
- claude_mpm/services/profile_manager.py +337 -0
- claude_mpm/services/skills/git_skill_source_manager.py +148 -11
- claude_mpm/services/skills/selective_skill_deployer.py +97 -48
- claude_mpm/services/skills_deployer.py +161 -65
- claude_mpm/services/socketio/dashboard_server.py +1 -0
- claude_mpm/services/socketio/event_normalizer.py +37 -6
- claude_mpm/services/socketio/server/core.py +262 -123
- claude_mpm/skills/bundled/security-scanning.md +112 -0
- claude_mpm/skills/skill_manager.py +98 -3
- claude_mpm/templates/.pre-commit-config.yaml +112 -0
- claude_mpm/utils/agent_dependency_loader.py +14 -2
- claude_mpm/utils/agent_filters.py +1 -1
- claude_mpm/utils/migration.py +4 -4
- claude_mpm/utils/robust_installer.py +47 -3
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/METADATA +7 -4
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/RECORD +175 -81
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/top_level.txt +0 -0
|
@@ -551,38 +551,39 @@ class AsyncAgentDeploymentService:
|
|
|
551
551
|
or ["Read", "Write", "Edit", "Grep", "Glob", "LS"] # Default fallback
|
|
552
552
|
)
|
|
553
553
|
|
|
554
|
-
# Get model from capabilities.model in new format
|
|
554
|
+
# Get model from capabilities.model in new format (no default fallback)
|
|
555
555
|
model = (
|
|
556
556
|
agent_data.get("capabilities", {}).get("model")
|
|
557
557
|
or agent_data.get("configuration_fields", {}).get("model")
|
|
558
|
-
|
|
558
|
+
# No default fallback - preserve None if not set
|
|
559
559
|
)
|
|
560
560
|
|
|
561
|
-
# Simplify model name for Claude Code
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
if
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
561
|
+
# Simplify model name for Claude Code (only if model is specified)
|
|
562
|
+
if model is not None:
|
|
563
|
+
model_map = {
|
|
564
|
+
"claude-4-sonnet-20250514": "sonnet",
|
|
565
|
+
"claude-sonnet-4-20250514": "sonnet",
|
|
566
|
+
"claude-opus-4-20250514": "opus",
|
|
567
|
+
"claude-3-opus-20240229": "opus",
|
|
568
|
+
"claude-3-haiku-20240307": "haiku",
|
|
569
|
+
"claude-3.5-sonnet": "sonnet",
|
|
570
|
+
"claude-3-sonnet": "sonnet",
|
|
571
|
+
}
|
|
572
|
+
# Better fallback: extract the model type (opus/sonnet/haiku) from the string
|
|
573
|
+
if model not in model_map:
|
|
574
|
+
if "opus" in model.lower():
|
|
575
|
+
model = "opus"
|
|
576
|
+
elif "sonnet" in model.lower():
|
|
577
|
+
model = "sonnet"
|
|
578
|
+
elif "haiku" in model.lower():
|
|
579
|
+
model = "haiku"
|
|
580
|
+
else:
|
|
581
|
+
# Last resort: try to extract from hyphenated format
|
|
582
|
+
model = model_map.get(
|
|
583
|
+
model, model.split("-")[-1] if "-" in model else model
|
|
584
|
+
)
|
|
579
585
|
else:
|
|
580
|
-
|
|
581
|
-
model = model_map.get(
|
|
582
|
-
model, model.split("-")[-1] if "-" in model else model
|
|
583
|
-
)
|
|
584
|
-
else:
|
|
585
|
-
model = model_map[model]
|
|
586
|
+
model = model_map[model]
|
|
586
587
|
|
|
587
588
|
# Convert tools list to comma-separated string for Claude Code compatibility
|
|
588
589
|
# IMPORTANT: No spaces after commas - Claude Code requires exact format
|
|
@@ -601,9 +602,12 @@ class AsyncAgentDeploymentService:
|
|
|
601
602
|
f"base_version: {self._format_version_display(base_version)}",
|
|
602
603
|
"author: claude-mpm", # Identify as system agent for deployment
|
|
603
604
|
f"tools: {tools_str}",
|
|
604
|
-
f"model: {model}",
|
|
605
605
|
]
|
|
606
606
|
|
|
607
|
+
# Only include model field if explicitly set
|
|
608
|
+
if model is not None:
|
|
609
|
+
frontmatter_lines.append(f"model: {model}")
|
|
610
|
+
|
|
607
611
|
# Add optional fields if present
|
|
608
612
|
# Check for color in metadata section (new format) or root (old format)
|
|
609
613
|
color = agent_data.get("metadata", {}).get("color") or agent_data.get("color")
|
|
@@ -157,7 +157,9 @@ class LocalTemplateDeploymentService:
|
|
|
157
157
|
|
|
158
158
|
# Add capabilities
|
|
159
159
|
if template.capabilities:
|
|
160
|
-
|
|
160
|
+
# Only include model if explicitly set (no default)
|
|
161
|
+
if "model" in template.capabilities:
|
|
162
|
+
frontmatter["model"] = template.capabilities["model"]
|
|
161
163
|
tools = template.capabilities.get("tools", "*")
|
|
162
164
|
if tools == "*":
|
|
163
165
|
frontmatter["tools"] = "all"
|
|
@@ -26,6 +26,18 @@ from .agent_version_manager import AgentVersionManager
|
|
|
26
26
|
from .remote_agent_discovery_service import RemoteAgentDiscoveryService
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def _normalize_agent_name(name: str) -> str:
|
|
30
|
+
"""Normalize agent name for consistent comparison.
|
|
31
|
+
|
|
32
|
+
Converts spaces, underscores to hyphens and lowercases.
|
|
33
|
+
Examples:
|
|
34
|
+
"Dart Engineer" -> "dart-engineer"
|
|
35
|
+
"dart_engineer" -> "dart-engineer"
|
|
36
|
+
"DART-ENGINEER" -> "dart-engineer"
|
|
37
|
+
"""
|
|
38
|
+
return name.lower().replace(" ", "-").replace("_", "-")
|
|
39
|
+
|
|
40
|
+
|
|
29
41
|
class MultiSourceAgentDeploymentService:
|
|
30
42
|
"""Service for deploying agents from multiple sources with version comparison.
|
|
31
43
|
|
|
@@ -179,14 +191,14 @@ class MultiSourceAgentDeploymentService:
|
|
|
179
191
|
system_templates_dir: Optional[Path] = None,
|
|
180
192
|
project_agents_dir: Optional[Path] = None,
|
|
181
193
|
user_agents_dir: Optional[Path] = None,
|
|
182
|
-
|
|
194
|
+
agents_cache_dir: Optional[Path] = None,
|
|
183
195
|
working_directory: Optional[Path] = None,
|
|
184
196
|
) -> Dict[str, List[Dict[str, Any]]]:
|
|
185
|
-
"""Discover agents from all 4 tiers (system, user,
|
|
197
|
+
"""Discover agents from all 4 tiers (system, user, cache, project).
|
|
186
198
|
|
|
187
199
|
Priority hierarchy (highest to lowest):
|
|
188
200
|
4. Project agents - Highest priority, project-specific customizations
|
|
189
|
-
3.
|
|
201
|
+
3. Cached agents - GitHub-synced agents from cache
|
|
190
202
|
2. User agents - DEPRECATED, user-level customizations
|
|
191
203
|
1. System templates - Lowest priority, built-in agents
|
|
192
204
|
|
|
@@ -194,7 +206,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
194
206
|
system_templates_dir: Directory containing system agent templates
|
|
195
207
|
project_agents_dir: Directory containing project-specific agents
|
|
196
208
|
user_agents_dir: Directory containing user custom agents (DEPRECATED)
|
|
197
|
-
|
|
209
|
+
agents_cache_dir: Directory containing cached agents from Git sources
|
|
198
210
|
working_directory: Current working directory for finding project agents
|
|
199
211
|
|
|
200
212
|
Returns:
|
|
@@ -225,12 +237,12 @@ class MultiSourceAgentDeploymentService:
|
|
|
225
237
|
if not user_agents_dir.exists():
|
|
226
238
|
user_agents_dir = None
|
|
227
239
|
|
|
228
|
-
if not
|
|
229
|
-
# Check for
|
|
240
|
+
if not agents_cache_dir:
|
|
241
|
+
# Check for agents in cache directory
|
|
230
242
|
cache_dir = Path.home() / ".claude-mpm" / "cache"
|
|
231
|
-
|
|
232
|
-
if not
|
|
233
|
-
|
|
243
|
+
agents_cache_dir = cache_dir / "agents"
|
|
244
|
+
if not agents_cache_dir.exists():
|
|
245
|
+
agents_cache_dir = None
|
|
234
246
|
|
|
235
247
|
# Discover agents from each source in priority order
|
|
236
248
|
# Note: We process in reverse priority order (system first) and build up the dictionary
|
|
@@ -238,7 +250,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
238
250
|
sources = [
|
|
239
251
|
("system", system_templates_dir),
|
|
240
252
|
("user", user_agents_dir),
|
|
241
|
-
("remote",
|
|
253
|
+
("remote", agents_cache_dir),
|
|
242
254
|
("project", project_agents_dir),
|
|
243
255
|
]
|
|
244
256
|
|
|
@@ -323,7 +335,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
323
335
|
def get_agents_by_collection(
|
|
324
336
|
self,
|
|
325
337
|
collection_id: str,
|
|
326
|
-
|
|
338
|
+
agents_cache_dir: Optional[Path] = None,
|
|
327
339
|
) -> List[Dict[str, Any]]:
|
|
328
340
|
"""Get all agents from a specific collection.
|
|
329
341
|
|
|
@@ -331,7 +343,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
331
343
|
|
|
332
344
|
Args:
|
|
333
345
|
collection_id: Collection identifier (e.g., "bobmatnyc/claude-mpm-agents")
|
|
334
|
-
|
|
346
|
+
agents_cache_dir: Directory containing agents cache
|
|
335
347
|
|
|
336
348
|
Returns:
|
|
337
349
|
List of agent dictionaries from the specified collection
|
|
@@ -342,18 +354,16 @@ class MultiSourceAgentDeploymentService:
|
|
|
342
354
|
>>> len(agents)
|
|
343
355
|
45
|
|
344
356
|
"""
|
|
345
|
-
if not
|
|
357
|
+
if not agents_cache_dir:
|
|
346
358
|
cache_dir = Path.home() / ".claude-mpm" / "cache"
|
|
347
|
-
|
|
359
|
+
agents_cache_dir = cache_dir / "agents"
|
|
348
360
|
|
|
349
|
-
if not
|
|
350
|
-
self.logger.warning(
|
|
351
|
-
f"Remote agents directory not found: {remote_agents_dir}"
|
|
352
|
-
)
|
|
361
|
+
if not agents_cache_dir.exists():
|
|
362
|
+
self.logger.warning(f"Agents cache directory not found: {agents_cache_dir}")
|
|
353
363
|
return []
|
|
354
364
|
|
|
355
365
|
# Use RemoteAgentDiscoveryService to get collection agents
|
|
356
|
-
remote_service = RemoteAgentDiscoveryService(
|
|
366
|
+
remote_service = RemoteAgentDiscoveryService(agents_cache_dir)
|
|
357
367
|
collection_agents = remote_service.get_agents_by_collection(collection_id)
|
|
358
368
|
|
|
359
369
|
self.logger.info(
|
|
@@ -470,7 +480,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
470
480
|
system_templates_dir: Optional[Path] = None,
|
|
471
481
|
project_agents_dir: Optional[Path] = None,
|
|
472
482
|
user_agents_dir: Optional[Path] = None,
|
|
473
|
-
|
|
483
|
+
agents_cache_dir: Optional[Path] = None,
|
|
474
484
|
working_directory: Optional[Path] = None,
|
|
475
485
|
excluded_agents: Optional[List[str]] = None,
|
|
476
486
|
config: Optional[Config] = None,
|
|
@@ -482,7 +492,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
482
492
|
system_templates_dir: Directory containing system agent templates
|
|
483
493
|
project_agents_dir: Directory containing project-specific agents
|
|
484
494
|
user_agents_dir: Directory containing user custom agents (DEPRECATED)
|
|
485
|
-
|
|
495
|
+
agents_cache_dir: Directory containing cached agents from Git sources
|
|
486
496
|
working_directory: Current working directory for finding project agents
|
|
487
497
|
excluded_agents: List of agent names to exclude from deployment
|
|
488
498
|
config: Configuration object for additional filtering
|
|
@@ -499,7 +509,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
499
509
|
system_templates_dir=system_templates_dir,
|
|
500
510
|
project_agents_dir=project_agents_dir,
|
|
501
511
|
user_agents_dir=user_agents_dir,
|
|
502
|
-
|
|
512
|
+
agents_cache_dir=agents_cache_dir,
|
|
503
513
|
working_directory=working_directory,
|
|
504
514
|
)
|
|
505
515
|
|
|
@@ -533,10 +543,42 @@ class MultiSourceAgentDeploymentService:
|
|
|
533
543
|
|
|
534
544
|
# Apply exclusion filters
|
|
535
545
|
if excluded_agents:
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
546
|
+
# Find agents to remove by matching normalized names
|
|
547
|
+
# Normalization handles: "Dart Engineer", "dart_engineer", "dart-engineer"
|
|
548
|
+
agents_to_remove = []
|
|
549
|
+
excluded_set = {_normalize_agent_name(name) for name in excluded_agents}
|
|
550
|
+
|
|
551
|
+
for canonical_id, agent_info in list(selected_agents.items()):
|
|
552
|
+
# Check agent name field (normalized)
|
|
553
|
+
agent_name = _normalize_agent_name(agent_info.get("name", ""))
|
|
554
|
+
|
|
555
|
+
# Also check the agent_id portion of canonical_id (after the colon)
|
|
556
|
+
# Example: "bobmatnyc/claude-mpm-agents:pm" -> "pm"
|
|
557
|
+
raw_agent_id = (
|
|
558
|
+
canonical_id.split(":")[-1] if ":" in canonical_id else canonical_id
|
|
559
|
+
)
|
|
560
|
+
agent_id = _normalize_agent_name(raw_agent_id)
|
|
561
|
+
|
|
562
|
+
# Check file stem from path (most reliable match)
|
|
563
|
+
file_stem = ""
|
|
564
|
+
path_str = agent_info.get("path") or agent_info.get("file_path")
|
|
565
|
+
if path_str:
|
|
566
|
+
file_stem = _normalize_agent_name(Path(path_str).stem)
|
|
567
|
+
|
|
568
|
+
if (
|
|
569
|
+
agent_name in excluded_set
|
|
570
|
+
or agent_id in excluded_set
|
|
571
|
+
or file_stem in excluded_set
|
|
572
|
+
):
|
|
573
|
+
agents_to_remove.append(canonical_id)
|
|
574
|
+
self.logger.info(
|
|
575
|
+
f"Excluding agent '{agent_info.get('name', raw_agent_id)}' "
|
|
576
|
+
f"(canonical_id: {canonical_id}) from deployment"
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
# Remove matched agents
|
|
580
|
+
for canonical_id in agents_to_remove:
|
|
581
|
+
del selected_agents[canonical_id]
|
|
540
582
|
|
|
541
583
|
# Apply config-based filtering if provided
|
|
542
584
|
if config:
|
|
@@ -585,6 +627,107 @@ class MultiSourceAgentDeploymentService:
|
|
|
585
627
|
|
|
586
628
|
return agents_to_deploy, agent_sources, cleanup_results
|
|
587
629
|
|
|
630
|
+
def cleanup_excluded_agents(
|
|
631
|
+
self,
|
|
632
|
+
deployed_agents_dir: Path,
|
|
633
|
+
agents_to_deploy: Dict[str, Path],
|
|
634
|
+
) -> Dict[str, Any]:
|
|
635
|
+
"""Remove agents from deployed directory that aren't in the deployment list.
|
|
636
|
+
|
|
637
|
+
Similar to skill cleanup logic, this removes agents that were previously
|
|
638
|
+
deployed but are no longer in the enabled agents list (e.g., filtered out
|
|
639
|
+
by profile configuration).
|
|
640
|
+
|
|
641
|
+
Args:
|
|
642
|
+
deployed_agents_dir: Directory containing deployed agents (~/.claude/agents)
|
|
643
|
+
agents_to_deploy: Dictionary mapping agent file stems to template paths
|
|
644
|
+
|
|
645
|
+
Returns:
|
|
646
|
+
Dictionary with cleanup results:
|
|
647
|
+
- removed: List of removed agent names
|
|
648
|
+
- errors: List of errors during cleanup
|
|
649
|
+
"""
|
|
650
|
+
cleanup_results = {"removed": [], "errors": []}
|
|
651
|
+
|
|
652
|
+
# Safety check - only operate on deployed agents directory
|
|
653
|
+
if not deployed_agents_dir.exists():
|
|
654
|
+
self.logger.debug(
|
|
655
|
+
"Deployed agents directory does not exist, no cleanup needed"
|
|
656
|
+
)
|
|
657
|
+
return cleanup_results
|
|
658
|
+
|
|
659
|
+
# Build set of agent names that should exist (file stems without .md extension)
|
|
660
|
+
expected_agents = set(agents_to_deploy.keys())
|
|
661
|
+
|
|
662
|
+
try:
|
|
663
|
+
# Check each file in deployed_agents_dir
|
|
664
|
+
for item in deployed_agents_dir.iterdir():
|
|
665
|
+
# Only process .md files
|
|
666
|
+
if not item.is_file() or item.suffix != ".md":
|
|
667
|
+
continue
|
|
668
|
+
|
|
669
|
+
# Skip hidden files
|
|
670
|
+
if item.name.startswith("."):
|
|
671
|
+
continue
|
|
672
|
+
|
|
673
|
+
# Get agent name (file stem)
|
|
674
|
+
agent_name = item.stem
|
|
675
|
+
|
|
676
|
+
# Check if this agent should be kept
|
|
677
|
+
if agent_name not in expected_agents:
|
|
678
|
+
try:
|
|
679
|
+
# Security: Validate path is within deployed_agents_dir
|
|
680
|
+
resolved_item = item.resolve()
|
|
681
|
+
resolved_target = deployed_agents_dir.resolve()
|
|
682
|
+
|
|
683
|
+
if not str(resolved_item).startswith(str(resolved_target)):
|
|
684
|
+
self.logger.error(
|
|
685
|
+
f"Refusing to remove path outside target directory: {item}"
|
|
686
|
+
)
|
|
687
|
+
cleanup_results["errors"].append(
|
|
688
|
+
{
|
|
689
|
+
"agent": agent_name,
|
|
690
|
+
"error": "Path outside target directory",
|
|
691
|
+
}
|
|
692
|
+
)
|
|
693
|
+
continue
|
|
694
|
+
|
|
695
|
+
# Remove the agent file
|
|
696
|
+
item.unlink()
|
|
697
|
+
cleanup_results["removed"].append(agent_name)
|
|
698
|
+
self.logger.info(f"Removed excluded agent: {agent_name}")
|
|
699
|
+
|
|
700
|
+
except PermissionError as e:
|
|
701
|
+
error_msg = f"Permission denied removing {agent_name}: {e}"
|
|
702
|
+
self.logger.error(error_msg)
|
|
703
|
+
cleanup_results["errors"].append(
|
|
704
|
+
{"agent": agent_name, "error": error_msg}
|
|
705
|
+
)
|
|
706
|
+
except Exception as e:
|
|
707
|
+
error_msg = f"Error removing {agent_name}: {e}"
|
|
708
|
+
self.logger.error(error_msg)
|
|
709
|
+
cleanup_results["errors"].append(
|
|
710
|
+
{"agent": agent_name, "error": error_msg}
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
except Exception as e:
|
|
714
|
+
self.logger.error(f"Error during agent cleanup: {e}")
|
|
715
|
+
cleanup_results["errors"].append(
|
|
716
|
+
{"agent": "cleanup_process", "error": str(e)}
|
|
717
|
+
)
|
|
718
|
+
|
|
719
|
+
# Log cleanup summary
|
|
720
|
+
if cleanup_results["removed"]:
|
|
721
|
+
self.logger.info(
|
|
722
|
+
f"Cleanup complete: removed {len(cleanup_results['removed'])} excluded agents"
|
|
723
|
+
)
|
|
724
|
+
if cleanup_results["errors"]:
|
|
725
|
+
self.logger.warning(
|
|
726
|
+
f"Encountered {len(cleanup_results['errors'])} errors during cleanup"
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
return cleanup_results
|
|
730
|
+
|
|
588
731
|
def cleanup_outdated_user_agents(
|
|
589
732
|
self,
|
|
590
733
|
agents_by_name: Dict[str, List[Dict[str, Any]]],
|