claude-mpm 5.1.9__py3-none-any.whl → 5.4.48__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/__init__.py +4 -0
- claude_mpm/agents/BASE_AGENT.md +164 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
- claude_mpm/agents/MEMORY.md +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +843 -900
- claude_mpm/agents/WORKFLOW.md +5 -254
- claude_mpm/agents/agent_loader.py +13 -44
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/frontmatter_validator.py +2 -2
- claude_mpm/agents/templates/circuit-breakers.md +138 -1
- claude_mpm/cli/__main__.py +4 -0
- claude_mpm/cli/chrome_devtools_installer.py +175 -0
- claude_mpm/cli/commands/agent_state_manager.py +18 -27
- claude_mpm/cli/commands/agents.py +9 -40
- claude_mpm/cli/commands/auto_configure.py +210 -25
- claude_mpm/cli/commands/config.py +88 -2
- claude_mpm/cli/commands/configure.py +1098 -159
- claude_mpm/cli/commands/configure_agent_display.py +25 -6
- claude_mpm/cli/commands/mpm_init/core.py +225 -46
- claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
- claude_mpm/cli/commands/postmortem.py +1 -1
- claude_mpm/cli/commands/profile.py +277 -0
- claude_mpm/cli/commands/skills.py +218 -197
- claude_mpm/cli/commands/summarize.py +413 -0
- claude_mpm/cli/executor.py +21 -3
- claude_mpm/cli/interactive/agent_wizard.py +2 -2
- claude_mpm/cli/parsers/agents_parser.py +0 -9
- claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
- claude_mpm/cli/parsers/base_parser.py +12 -0
- claude_mpm/cli/parsers/config_parser.py +153 -83
- claude_mpm/cli/parsers/profile_parser.py +148 -0
- claude_mpm/cli/parsers/skills_parser.py +0 -5
- claude_mpm/cli/startup.py +876 -149
- claude_mpm/commands/mpm-config.md +28 -0
- claude_mpm/commands/mpm-doctor.md +9 -22
- claude_mpm/commands/mpm-help.md +5 -287
- claude_mpm/commands/mpm-init.md +81 -507
- claude_mpm/commands/mpm-monitor.md +15 -402
- claude_mpm/commands/mpm-organize.md +120 -0
- 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/config/agent_sources.py +27 -0
- claude_mpm/core/config.py +2 -4
- claude_mpm/core/framework/formatters/content_formatter.py +3 -13
- claude_mpm/core/framework/loaders/agent_loader.py +8 -5
- claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
- claude_mpm/core/framework_loader.py +4 -2
- claude_mpm/core/logger.py +13 -0
- claude_mpm/core/optimized_startup.py +59 -0
- claude_mpm/core/shared/config_loader.py +1 -1
- claude_mpm/core/socketio_pool.py +3 -3
- claude_mpm/core/unified_agent_registry.py +5 -15
- claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -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/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/correlation_manager.py +60 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
- claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
- claude_mpm/hooks/claude_hooks/installer.py +33 -10
- claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
- claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.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 +30 -6
- claude_mpm/hooks/kuzu_memory_hook.py +5 -5
- claude_mpm/hooks/memory_integration_hook.py +46 -1
- claude_mpm/init.py +63 -19
- claude_mpm/models/git_repository.py +3 -3
- claude_mpm/scripts/claude-hook-handler.sh +58 -18
- claude_mpm/scripts/launch_monitor.py +93 -13
- claude_mpm/services/agents/agent_builder.py +3 -3
- claude_mpm/services/agents/agent_recommendation_service.py +278 -0
- claude_mpm/services/agents/agent_review_service.py +280 -0
- 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 -5
- claude_mpm/services/agents/deployment/agent_format_converter.py +23 -13
- claude_mpm/services/agents/deployment/agent_template_builder.py +32 -20
- 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 +247 -35
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +392 -87
- claude_mpm/services/agents/git_source_manager.py +53 -4
- claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
- 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 +120 -7
- claude_mpm/services/agents/startup_sync.py +22 -2
- claude_mpm/services/agents/toolchain_detector.py +10 -6
- claude_mpm/services/analysis/__init__.py +11 -1
- claude_mpm/services/analysis/clone_detector.py +1030 -0
- claude_mpm/services/command_deployment_service.py +81 -10
- claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
- claude_mpm/services/event_bus/config.py +3 -1
- claude_mpm/services/git/git_operations_service.py +101 -16
- claude_mpm/services/monitor/daemon.py +9 -2
- claude_mpm/services/monitor/daemon_manager.py +39 -3
- claude_mpm/services/monitor/management/lifecycle.py +8 -1
- claude_mpm/services/monitor/server.py +698 -22
- claude_mpm/services/pm_skills_deployer.py +711 -0
- claude_mpm/services/profile_manager.py +331 -0
- claude_mpm/services/self_upgrade_service.py +120 -12
- claude_mpm/services/skills/__init__.py +3 -0
- claude_mpm/services/skills/git_skill_source_manager.py +130 -2
- claude_mpm/services/skills/selective_skill_deployer.py +704 -0
- claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
- claude_mpm/services/skills_deployer.py +127 -9
- claude_mpm/services/socketio/dashboard_server.py +1 -0
- claude_mpm/services/socketio/event_normalizer.py +51 -6
- claude_mpm/services/socketio/server/core.py +386 -108
- claude_mpm/services/version_control/git_operations.py +103 -0
- claude_mpm/skills/skill_manager.py +92 -3
- claude_mpm/utils/agent_dependency_loader.py +14 -2
- claude_mpm/utils/agent_filters.py +17 -44
- claude_mpm/utils/migration.py +4 -4
- claude_mpm/utils/robust_installer.py +47 -3
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/METADATA +53 -87
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/RECORD +157 -197
- claude_mpm-5.4.48.dist-info/entry_points.txt +5 -0
- claude_mpm-5.4.48.dist-info/licenses/LICENSE +94 -0
- claude_mpm-5.4.48.dist-info/licenses/LICENSE-FAQ.md +153 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
- claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
- claude_mpm/agents/BASE_OPS.md +0 -219
- claude_mpm/agents/BASE_PM.md +0 -480
- claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
- claude_mpm/agents/BASE_QA.md +0 -167
- claude_mpm/agents/BASE_RESEARCH.md +0 -53
- claude_mpm/agents/base_agent_loader.py +0 -601
- claude_mpm/cli/commands/agents_detect.py +0 -380
- claude_mpm/cli/commands/agents_recommend.py +0 -309
- claude_mpm/cli/ticket_cli.py +0 -35
- claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
- claude_mpm/commands/mpm-agents-detect.md +0 -177
- claude_mpm/commands/mpm-agents-list.md +0 -131
- claude_mpm/commands/mpm-agents-recommend.md +0 -223
- claude_mpm/commands/mpm-config-view.md +0 -150
- claude_mpm/commands/mpm-ticket-organize.md +0 -304
- claude_mpm/dashboard/analysis_runner.py +0 -455
- claude_mpm/dashboard/index.html +0 -13
- claude_mpm/dashboard/open_dashboard.py +0 -66
- claude_mpm/dashboard/static/css/activity.css +0 -1958
- claude_mpm/dashboard/static/css/connection-status.css +0 -370
- claude_mpm/dashboard/static/css/dashboard.css +0 -4701
- claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
- claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
- claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
- claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
- claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
- claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
- claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
- claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
- claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
- claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
- claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
- claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
- claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
- claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
- claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
- claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
- claude_mpm/dashboard/static/js/connection-manager.js +0 -536
- claude_mpm/dashboard/static/js/dashboard.js +0 -1914
- claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
- claude_mpm/dashboard/static/js/socket-client.js +0 -1474
- claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
- claude_mpm/dashboard/static/socket.io.min.js +0 -7
- claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
- claude_mpm/dashboard/templates/code_simple.html +0 -153
- claude_mpm/dashboard/templates/index.html +0 -606
- claude_mpm/dashboard/test_dashboard.html +0 -372
- claude_mpm/scripts/mcp_server.py +0 -75
- claude_mpm/scripts/mcp_wrapper.py +0 -39
- claude_mpm/services/mcp_gateway/__init__.py +0 -159
- claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
- claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
- claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
- claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
- claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
- claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
- claude_mpm/services/mcp_gateway/core/base.py +0 -312
- claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
- claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
- claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
- claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
- claude_mpm/services/mcp_gateway/main.py +0 -589
- claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
- claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
- claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
- claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
- claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
- claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
- claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
- claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
- claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
- claude_mpm-5.1.9.dist-info/entry_points.txt +0 -10
- claude_mpm-5.1.9.dist-info/licenses/LICENSE +0 -21
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/WHEEL +0 -0
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.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"
|
|
@@ -16,6 +16,8 @@ import os
|
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
from typing import Any, Dict, List, Optional, Tuple
|
|
18
18
|
|
|
19
|
+
import yaml
|
|
20
|
+
|
|
19
21
|
from claude_mpm.core.config import Config
|
|
20
22
|
from claude_mpm.core.logging_config import get_logger
|
|
21
23
|
|
|
@@ -24,6 +26,18 @@ from .agent_version_manager import AgentVersionManager
|
|
|
24
26
|
from .remote_agent_discovery_service import RemoteAgentDiscoveryService
|
|
25
27
|
|
|
26
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
|
+
|
|
27
41
|
class MultiSourceAgentDeploymentService:
|
|
28
42
|
"""Service for deploying agents from multiple sources with version comparison.
|
|
29
43
|
|
|
@@ -51,6 +65,70 @@ class MultiSourceAgentDeploymentService:
|
|
|
51
65
|
self.logger = get_logger(__name__)
|
|
52
66
|
self.version_manager = AgentVersionManager()
|
|
53
67
|
|
|
68
|
+
def _read_template_version(self, template_path: Path) -> Optional[str]:
|
|
69
|
+
"""Read version from template file (supports both .md and .json formats).
|
|
70
|
+
|
|
71
|
+
For .md files: Extract version from YAML frontmatter
|
|
72
|
+
For .json files: Extract version from JSON structure
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
template_path: Path to template file
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Version string or None if version cannot be extracted
|
|
79
|
+
"""
|
|
80
|
+
try:
|
|
81
|
+
if template_path.suffix == ".md":
|
|
82
|
+
# Parse markdown with YAML frontmatter
|
|
83
|
+
content = template_path.read_text()
|
|
84
|
+
|
|
85
|
+
# Extract YAML frontmatter (between --- markers)
|
|
86
|
+
if not content.strip().startswith("---"):
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
parts = content.split("---", 2)
|
|
90
|
+
if len(parts) < 3:
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
# Parse YAML frontmatter
|
|
94
|
+
frontmatter = yaml.safe_load(parts[1])
|
|
95
|
+
if not frontmatter:
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
# Extract version from frontmatter
|
|
99
|
+
version = frontmatter.get("version")
|
|
100
|
+
return version if version else None
|
|
101
|
+
|
|
102
|
+
if template_path.suffix == ".json":
|
|
103
|
+
# Parse JSON template
|
|
104
|
+
template_data = json.loads(template_path.read_text())
|
|
105
|
+
metadata = template_data.get("metadata", {})
|
|
106
|
+
version = (
|
|
107
|
+
template_data.get("agent_version")
|
|
108
|
+
or template_data.get("version")
|
|
109
|
+
or metadata.get("version")
|
|
110
|
+
)
|
|
111
|
+
return version if version else None
|
|
112
|
+
|
|
113
|
+
self.logger.warning(
|
|
114
|
+
f"Unknown template format: {template_path.suffix} for {template_path.name}"
|
|
115
|
+
)
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
except yaml.YAMLError as e:
|
|
119
|
+
self.logger.warning(
|
|
120
|
+
f"Invalid YAML frontmatter in {template_path.name}: {e}"
|
|
121
|
+
)
|
|
122
|
+
return None
|
|
123
|
+
except json.JSONDecodeError as e:
|
|
124
|
+
self.logger.warning(f"Invalid JSON in {template_path.name}: {e}")
|
|
125
|
+
return None
|
|
126
|
+
except Exception as e:
|
|
127
|
+
self.logger.warning(
|
|
128
|
+
f"Error reading template version from {template_path.name}: {e}"
|
|
129
|
+
)
|
|
130
|
+
return None
|
|
131
|
+
|
|
54
132
|
def _build_canonical_id_for_agent(self, agent_info: Dict[str, Any]) -> str:
|
|
55
133
|
"""Build or retrieve canonical_id for an agent.
|
|
56
134
|
|
|
@@ -113,14 +191,14 @@ class MultiSourceAgentDeploymentService:
|
|
|
113
191
|
system_templates_dir: Optional[Path] = None,
|
|
114
192
|
project_agents_dir: Optional[Path] = None,
|
|
115
193
|
user_agents_dir: Optional[Path] = None,
|
|
116
|
-
|
|
194
|
+
agents_cache_dir: Optional[Path] = None,
|
|
117
195
|
working_directory: Optional[Path] = None,
|
|
118
196
|
) -> Dict[str, List[Dict[str, Any]]]:
|
|
119
|
-
"""Discover agents from all 4 tiers (system, user,
|
|
197
|
+
"""Discover agents from all 4 tiers (system, user, cache, project).
|
|
120
198
|
|
|
121
199
|
Priority hierarchy (highest to lowest):
|
|
122
200
|
4. Project agents - Highest priority, project-specific customizations
|
|
123
|
-
3.
|
|
201
|
+
3. Cached agents - GitHub-synced agents from cache
|
|
124
202
|
2. User agents - DEPRECATED, user-level customizations
|
|
125
203
|
1. System templates - Lowest priority, built-in agents
|
|
126
204
|
|
|
@@ -128,7 +206,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
128
206
|
system_templates_dir: Directory containing system agent templates
|
|
129
207
|
project_agents_dir: Directory containing project-specific agents
|
|
130
208
|
user_agents_dir: Directory containing user custom agents (DEPRECATED)
|
|
131
|
-
|
|
209
|
+
agents_cache_dir: Directory containing cached agents from Git sources
|
|
132
210
|
working_directory: Current working directory for finding project agents
|
|
133
211
|
|
|
134
212
|
Returns:
|
|
@@ -159,12 +237,12 @@ class MultiSourceAgentDeploymentService:
|
|
|
159
237
|
if not user_agents_dir.exists():
|
|
160
238
|
user_agents_dir = None
|
|
161
239
|
|
|
162
|
-
if not
|
|
163
|
-
# Check for
|
|
240
|
+
if not agents_cache_dir:
|
|
241
|
+
# Check for agents in cache directory
|
|
164
242
|
cache_dir = Path.home() / ".claude-mpm" / "cache"
|
|
165
|
-
|
|
166
|
-
if not
|
|
167
|
-
|
|
243
|
+
agents_cache_dir = cache_dir / "agents"
|
|
244
|
+
if not agents_cache_dir.exists():
|
|
245
|
+
agents_cache_dir = None
|
|
168
246
|
|
|
169
247
|
# Discover agents from each source in priority order
|
|
170
248
|
# Note: We process in reverse priority order (system first) and build up the dictionary
|
|
@@ -172,7 +250,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
172
250
|
sources = [
|
|
173
251
|
("system", system_templates_dir),
|
|
174
252
|
("user", user_agents_dir),
|
|
175
|
-
("remote",
|
|
253
|
+
("remote", agents_cache_dir),
|
|
176
254
|
("project", project_agents_dir),
|
|
177
255
|
]
|
|
178
256
|
|
|
@@ -257,7 +335,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
257
335
|
def get_agents_by_collection(
|
|
258
336
|
self,
|
|
259
337
|
collection_id: str,
|
|
260
|
-
|
|
338
|
+
agents_cache_dir: Optional[Path] = None,
|
|
261
339
|
) -> List[Dict[str, Any]]:
|
|
262
340
|
"""Get all agents from a specific collection.
|
|
263
341
|
|
|
@@ -265,7 +343,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
265
343
|
|
|
266
344
|
Args:
|
|
267
345
|
collection_id: Collection identifier (e.g., "bobmatnyc/claude-mpm-agents")
|
|
268
|
-
|
|
346
|
+
agents_cache_dir: Directory containing agents cache
|
|
269
347
|
|
|
270
348
|
Returns:
|
|
271
349
|
List of agent dictionaries from the specified collection
|
|
@@ -276,18 +354,16 @@ class MultiSourceAgentDeploymentService:
|
|
|
276
354
|
>>> len(agents)
|
|
277
355
|
45
|
|
278
356
|
"""
|
|
279
|
-
if not
|
|
357
|
+
if not agents_cache_dir:
|
|
280
358
|
cache_dir = Path.home() / ".claude-mpm" / "cache"
|
|
281
|
-
|
|
359
|
+
agents_cache_dir = cache_dir / "agents"
|
|
282
360
|
|
|
283
|
-
if not
|
|
284
|
-
self.logger.warning(
|
|
285
|
-
f"Remote agents directory not found: {remote_agents_dir}"
|
|
286
|
-
)
|
|
361
|
+
if not agents_cache_dir.exists():
|
|
362
|
+
self.logger.warning(f"Agents cache directory not found: {agents_cache_dir}")
|
|
287
363
|
return []
|
|
288
364
|
|
|
289
365
|
# Use RemoteAgentDiscoveryService to get collection agents
|
|
290
|
-
remote_service = RemoteAgentDiscoveryService(
|
|
366
|
+
remote_service = RemoteAgentDiscoveryService(agents_cache_dir)
|
|
291
367
|
collection_agents = remote_service.get_agents_by_collection(collection_id)
|
|
292
368
|
|
|
293
369
|
self.logger.info(
|
|
@@ -404,7 +480,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
404
480
|
system_templates_dir: Optional[Path] = None,
|
|
405
481
|
project_agents_dir: Optional[Path] = None,
|
|
406
482
|
user_agents_dir: Optional[Path] = None,
|
|
407
|
-
|
|
483
|
+
agents_cache_dir: Optional[Path] = None,
|
|
408
484
|
working_directory: Optional[Path] = None,
|
|
409
485
|
excluded_agents: Optional[List[str]] = None,
|
|
410
486
|
config: Optional[Config] = None,
|
|
@@ -416,7 +492,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
416
492
|
system_templates_dir: Directory containing system agent templates
|
|
417
493
|
project_agents_dir: Directory containing project-specific agents
|
|
418
494
|
user_agents_dir: Directory containing user custom agents (DEPRECATED)
|
|
419
|
-
|
|
495
|
+
agents_cache_dir: Directory containing cached agents from Git sources
|
|
420
496
|
working_directory: Current working directory for finding project agents
|
|
421
497
|
excluded_agents: List of agent names to exclude from deployment
|
|
422
498
|
config: Configuration object for additional filtering
|
|
@@ -433,7 +509,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
433
509
|
system_templates_dir=system_templates_dir,
|
|
434
510
|
project_agents_dir=project_agents_dir,
|
|
435
511
|
user_agents_dir=user_agents_dir,
|
|
436
|
-
|
|
512
|
+
agents_cache_dir=agents_cache_dir,
|
|
437
513
|
working_directory=working_directory,
|
|
438
514
|
)
|
|
439
515
|
|
|
@@ -467,10 +543,44 @@ class MultiSourceAgentDeploymentService:
|
|
|
467
543
|
|
|
468
544
|
# Apply exclusion filters
|
|
469
545
|
if excluded_agents:
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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]
|
|
559
|
+
if ":" in canonical_id
|
|
560
|
+
else canonical_id
|
|
561
|
+
)
|
|
562
|
+
agent_id = _normalize_agent_name(raw_agent_id)
|
|
563
|
+
|
|
564
|
+
# Check file stem from path (most reliable match)
|
|
565
|
+
file_stem = ""
|
|
566
|
+
path_str = agent_info.get("path") or agent_info.get("file_path")
|
|
567
|
+
if path_str:
|
|
568
|
+
file_stem = _normalize_agent_name(Path(path_str).stem)
|
|
569
|
+
|
|
570
|
+
if (
|
|
571
|
+
agent_name in excluded_set
|
|
572
|
+
or agent_id in excluded_set
|
|
573
|
+
or file_stem in excluded_set
|
|
574
|
+
):
|
|
575
|
+
agents_to_remove.append(canonical_id)
|
|
576
|
+
self.logger.info(
|
|
577
|
+
f"Excluding agent '{agent_info.get('name', raw_agent_id)}' "
|
|
578
|
+
f"(canonical_id: {canonical_id}) from deployment"
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
# Remove matched agents
|
|
582
|
+
for canonical_id in agents_to_remove:
|
|
583
|
+
del selected_agents[canonical_id]
|
|
474
584
|
|
|
475
585
|
# Apply config-based filtering if provided
|
|
476
586
|
if config:
|
|
@@ -519,6 +629,105 @@ class MultiSourceAgentDeploymentService:
|
|
|
519
629
|
|
|
520
630
|
return agents_to_deploy, agent_sources, cleanup_results
|
|
521
631
|
|
|
632
|
+
def cleanup_excluded_agents(
|
|
633
|
+
self,
|
|
634
|
+
deployed_agents_dir: Path,
|
|
635
|
+
agents_to_deploy: Dict[str, Path],
|
|
636
|
+
) -> Dict[str, Any]:
|
|
637
|
+
"""Remove agents from deployed directory that aren't in the deployment list.
|
|
638
|
+
|
|
639
|
+
Similar to skill cleanup logic, this removes agents that were previously
|
|
640
|
+
deployed but are no longer in the enabled agents list (e.g., filtered out
|
|
641
|
+
by profile configuration).
|
|
642
|
+
|
|
643
|
+
Args:
|
|
644
|
+
deployed_agents_dir: Directory containing deployed agents (~/.claude/agents)
|
|
645
|
+
agents_to_deploy: Dictionary mapping agent file stems to template paths
|
|
646
|
+
|
|
647
|
+
Returns:
|
|
648
|
+
Dictionary with cleanup results:
|
|
649
|
+
- removed: List of removed agent names
|
|
650
|
+
- errors: List of errors during cleanup
|
|
651
|
+
"""
|
|
652
|
+
cleanup_results = {"removed": [], "errors": []}
|
|
653
|
+
|
|
654
|
+
# Safety check - only operate on deployed agents directory
|
|
655
|
+
if not deployed_agents_dir.exists():
|
|
656
|
+
self.logger.debug("Deployed agents directory does not exist, no cleanup needed")
|
|
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
|
+
|
|
522
731
|
def cleanup_outdated_user_agents(
|
|
523
732
|
self,
|
|
524
733
|
agents_by_name: Dict[str, List[Dict[str, Any]]],
|
|
@@ -827,17 +1036,20 @@ class MultiSourceAgentDeploymentService:
|
|
|
827
1036
|
comparison_results["needs_update"].append(agent_name)
|
|
828
1037
|
continue
|
|
829
1038
|
|
|
830
|
-
# Read template version
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
template_data.get("agent_version")
|
|
836
|
-
or template_data.get("version")
|
|
837
|
-
or metadata.get("version", "0.0.0")
|
|
1039
|
+
# Read template version using format-aware helper
|
|
1040
|
+
version_string = self._read_template_version(template_path)
|
|
1041
|
+
if not version_string:
|
|
1042
|
+
self.logger.warning(
|
|
1043
|
+
f"Could not extract version from template for '{agent_name}', skipping"
|
|
838
1044
|
)
|
|
1045
|
+
continue
|
|
1046
|
+
|
|
1047
|
+
try:
|
|
1048
|
+
template_version = self.version_manager.parse_version(version_string)
|
|
839
1049
|
except Exception as e:
|
|
840
|
-
self.logger.warning(
|
|
1050
|
+
self.logger.warning(
|
|
1051
|
+
f"Error parsing version '{version_string}' for '{agent_name}': {e}"
|
|
1052
|
+
)
|
|
841
1053
|
continue
|
|
842
1054
|
|
|
843
1055
|
# Read deployed version
|