claude-mpm 5.0.9__py3-none-any.whl → 5.4.41__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/{PM_INSTRUCTIONS_TEACH.md → CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md} +721 -41
- claude_mpm/agents/MEMORY.md +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +468 -468
- 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 +70 -2
- claude_mpm/agents/templates/circuit-breakers.md +431 -45
- claude_mpm/cli/__init__.py +0 -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 +175 -37
- claude_mpm/cli/commands/auto_configure.py +723 -236
- claude_mpm/cli/commands/config.py +88 -2
- claude_mpm/cli/commands/configure.py +1262 -157
- 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 +214 -189
- claude_mpm/cli/commands/summarize.py +413 -0
- claude_mpm/cli/executor.py +21 -3
- claude_mpm/cli/interactive/agent_wizard.py +85 -10
- claude_mpm/cli/parsers/agents_parser.py +54 -9
- claude_mpm/cli/parsers/auto_configure_parser.py +13 -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 +3 -2
- claude_mpm/cli/startup.py +879 -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/output_style_manager.py +173 -43
- claude_mpm/core/shared/config_loader.py +1 -1
- claude_mpm/core/socketio_pool.py +3 -3
- claude_mpm/core/unified_agent_registry.py +134 -16
- claude_mpm/core/unified_config.py +22 -0
- 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 +28 -0
- 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/memory_integration_hook.py +46 -1
- claude_mpm/init.py +63 -19
- claude_mpm/models/agent_definition.py +7 -0
- 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/scripts/start_activity_logging.py +0 -0
- 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_template_builder.py +5 -3
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +320 -29
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +546 -68
- claude_mpm/services/agents/git_source_manager.py +36 -2
- 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 +13 -6
- 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 +676 -0
- claude_mpm/services/profile_manager.py +331 -0
- claude_mpm/services/project/project_organizer.py +4 -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 +126 -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/gitignore.py +3 -0
- claude_mpm/utils/migration.py +4 -4
- claude_mpm/utils/robust_installer.py +47 -3
- {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/METADATA +57 -87
- {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/RECORD +160 -211
- claude_mpm-5.4.41.dist-info/entry_points.txt +5 -0
- claude_mpm-5.4.41.dist-info/licenses/LICENSE +94 -0
- claude_mpm-5.4.41.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/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__/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_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/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.0.9.dist-info/entry_points.txt +0 -10
- claude_mpm-5.0.9.dist-info/licenses/LICENSE +0 -21
- /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
- {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/WHEEL +0 -0
- {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|
|
@@ -51,19 +53,140 @@ class MultiSourceAgentDeploymentService:
|
|
|
51
53
|
self.logger = get_logger(__name__)
|
|
52
54
|
self.version_manager = AgentVersionManager()
|
|
53
55
|
|
|
56
|
+
def _read_template_version(self, template_path: Path) -> Optional[str]:
|
|
57
|
+
"""Read version from template file (supports both .md and .json formats).
|
|
58
|
+
|
|
59
|
+
For .md files: Extract version from YAML frontmatter
|
|
60
|
+
For .json files: Extract version from JSON structure
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
template_path: Path to template file
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Version string or None if version cannot be extracted
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
if template_path.suffix == ".md":
|
|
70
|
+
# Parse markdown with YAML frontmatter
|
|
71
|
+
content = template_path.read_text()
|
|
72
|
+
|
|
73
|
+
# Extract YAML frontmatter (between --- markers)
|
|
74
|
+
if not content.strip().startswith("---"):
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
parts = content.split("---", 2)
|
|
78
|
+
if len(parts) < 3:
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
# Parse YAML frontmatter
|
|
82
|
+
frontmatter = yaml.safe_load(parts[1])
|
|
83
|
+
if not frontmatter:
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
# Extract version from frontmatter
|
|
87
|
+
version = frontmatter.get("version")
|
|
88
|
+
return version if version else None
|
|
89
|
+
|
|
90
|
+
if template_path.suffix == ".json":
|
|
91
|
+
# Parse JSON template
|
|
92
|
+
template_data = json.loads(template_path.read_text())
|
|
93
|
+
metadata = template_data.get("metadata", {})
|
|
94
|
+
version = (
|
|
95
|
+
template_data.get("agent_version")
|
|
96
|
+
or template_data.get("version")
|
|
97
|
+
or metadata.get("version")
|
|
98
|
+
)
|
|
99
|
+
return version if version else None
|
|
100
|
+
|
|
101
|
+
self.logger.warning(
|
|
102
|
+
f"Unknown template format: {template_path.suffix} for {template_path.name}"
|
|
103
|
+
)
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
except yaml.YAMLError as e:
|
|
107
|
+
self.logger.warning(
|
|
108
|
+
f"Invalid YAML frontmatter in {template_path.name}: {e}"
|
|
109
|
+
)
|
|
110
|
+
return None
|
|
111
|
+
except json.JSONDecodeError as e:
|
|
112
|
+
self.logger.warning(f"Invalid JSON in {template_path.name}: {e}")
|
|
113
|
+
return None
|
|
114
|
+
except Exception as e:
|
|
115
|
+
self.logger.warning(
|
|
116
|
+
f"Error reading template version from {template_path.name}: {e}"
|
|
117
|
+
)
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
def _build_canonical_id_for_agent(self, agent_info: Dict[str, Any]) -> str:
|
|
121
|
+
"""Build or retrieve canonical_id for an agent.
|
|
122
|
+
|
|
123
|
+
NEW: Supports enhanced agent matching via canonical_id.
|
|
124
|
+
|
|
125
|
+
Priority:
|
|
126
|
+
1. Use existing canonical_id from agent_info if present
|
|
127
|
+
2. Generate from collection_id + agent_id if available
|
|
128
|
+
3. Fallback to legacy:{filename} for backward compatibility
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
agent_info: Agent dictionary with metadata
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Canonical ID string for matching
|
|
135
|
+
|
|
136
|
+
Example:
|
|
137
|
+
Remote agent: "bobmatnyc/claude-mpm-agents:pm"
|
|
138
|
+
Legacy agent: "legacy:custom-agent"
|
|
139
|
+
"""
|
|
140
|
+
# Priority 1: Existing canonical_id
|
|
141
|
+
if "canonical_id" in agent_info:
|
|
142
|
+
return agent_info["canonical_id"]
|
|
143
|
+
|
|
144
|
+
# Priority 2: Generate from collection_id + agent_id
|
|
145
|
+
collection_id = agent_info.get("collection_id")
|
|
146
|
+
agent_id = agent_info.get("agent_id")
|
|
147
|
+
|
|
148
|
+
if collection_id and agent_id:
|
|
149
|
+
canonical_id = f"{collection_id}:{agent_id}"
|
|
150
|
+
# Cache it in agent_info for future use
|
|
151
|
+
agent_info["canonical_id"] = canonical_id
|
|
152
|
+
return canonical_id
|
|
153
|
+
|
|
154
|
+
# Priority 3: Fallback to legacy format
|
|
155
|
+
# Use filename or agent name
|
|
156
|
+
agent_name = agent_info.get("name") or agent_info.get("metadata", {}).get(
|
|
157
|
+
"name", "unknown"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Extract filename from path
|
|
161
|
+
path_str = (
|
|
162
|
+
agent_info.get("path")
|
|
163
|
+
or agent_info.get("file_path")
|
|
164
|
+
or agent_info.get("source_file")
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if path_str:
|
|
168
|
+
filename = Path(path_str).stem
|
|
169
|
+
canonical_id = f"legacy:{filename}"
|
|
170
|
+
else:
|
|
171
|
+
canonical_id = f"legacy:{agent_name}"
|
|
172
|
+
|
|
173
|
+
# Cache it
|
|
174
|
+
agent_info["canonical_id"] = canonical_id
|
|
175
|
+
return canonical_id
|
|
176
|
+
|
|
54
177
|
def discover_agents_from_all_sources(
|
|
55
178
|
self,
|
|
56
179
|
system_templates_dir: Optional[Path] = None,
|
|
57
180
|
project_agents_dir: Optional[Path] = None,
|
|
58
181
|
user_agents_dir: Optional[Path] = None,
|
|
59
|
-
|
|
182
|
+
agents_cache_dir: Optional[Path] = None,
|
|
60
183
|
working_directory: Optional[Path] = None,
|
|
61
184
|
) -> Dict[str, List[Dict[str, Any]]]:
|
|
62
|
-
"""Discover agents from all 4 tiers (system, user,
|
|
185
|
+
"""Discover agents from all 4 tiers (system, user, cache, project).
|
|
63
186
|
|
|
64
187
|
Priority hierarchy (highest to lowest):
|
|
65
188
|
4. Project agents - Highest priority, project-specific customizations
|
|
66
|
-
3.
|
|
189
|
+
3. Cached agents - GitHub-synced agents from cache
|
|
67
190
|
2. User agents - DEPRECATED, user-level customizations
|
|
68
191
|
1. System templates - Lowest priority, built-in agents
|
|
69
192
|
|
|
@@ -71,7 +194,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
71
194
|
system_templates_dir: Directory containing system agent templates
|
|
72
195
|
project_agents_dir: Directory containing project-specific agents
|
|
73
196
|
user_agents_dir: Directory containing user custom agents (DEPRECATED)
|
|
74
|
-
|
|
197
|
+
agents_cache_dir: Directory containing cached agents from Git sources
|
|
75
198
|
working_directory: Current working directory for finding project agents
|
|
76
199
|
|
|
77
200
|
Returns:
|
|
@@ -102,12 +225,12 @@ class MultiSourceAgentDeploymentService:
|
|
|
102
225
|
if not user_agents_dir.exists():
|
|
103
226
|
user_agents_dir = None
|
|
104
227
|
|
|
105
|
-
if not
|
|
106
|
-
# Check for
|
|
228
|
+
if not agents_cache_dir:
|
|
229
|
+
# Check for agents in cache directory
|
|
107
230
|
cache_dir = Path.home() / ".claude-mpm" / "cache"
|
|
108
|
-
|
|
109
|
-
if not
|
|
110
|
-
|
|
231
|
+
agents_cache_dir = cache_dir / "agents"
|
|
232
|
+
if not agents_cache_dir.exists():
|
|
233
|
+
agents_cache_dir = None
|
|
111
234
|
|
|
112
235
|
# Discover agents from each source in priority order
|
|
113
236
|
# Note: We process in reverse priority order (system first) and build up the dictionary
|
|
@@ -115,7 +238,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
115
238
|
sources = [
|
|
116
239
|
("system", system_templates_dir),
|
|
117
240
|
("user", user_agents_dir),
|
|
118
|
-
("remote",
|
|
241
|
+
("remote", agents_cache_dir),
|
|
119
242
|
("project", project_agents_dir),
|
|
120
243
|
]
|
|
121
244
|
|
|
@@ -156,11 +279,19 @@ class MultiSourceAgentDeploymentService:
|
|
|
156
279
|
agent_info["source"] = source_name
|
|
157
280
|
agent_info["source_dir"] = str(source_dir)
|
|
158
281
|
|
|
282
|
+
# NEW: Build canonical_id for enhanced matching
|
|
283
|
+
canonical_id = self._build_canonical_id_for_agent(agent_info)
|
|
284
|
+
|
|
285
|
+
# Group by canonical_id (PRIMARY) for enhanced matching
|
|
286
|
+
# This allows matching agents from different sources with same canonical_id
|
|
287
|
+
# while maintaining backward compatibility with name-based matching
|
|
288
|
+
matching_key = canonical_id
|
|
289
|
+
|
|
159
290
|
# Initialize list if this is the first occurrence of this agent
|
|
160
|
-
if
|
|
161
|
-
agents_by_name[
|
|
291
|
+
if matching_key not in agents_by_name:
|
|
292
|
+
agents_by_name[matching_key] = []
|
|
162
293
|
|
|
163
|
-
agents_by_name[
|
|
294
|
+
agents_by_name[matching_key].append(agent_info)
|
|
164
295
|
|
|
165
296
|
# Use more specific log message
|
|
166
297
|
self.logger.info(
|
|
@@ -189,6 +320,46 @@ class MultiSourceAgentDeploymentService:
|
|
|
189
320
|
|
|
190
321
|
return agents_by_name
|
|
191
322
|
|
|
323
|
+
def get_agents_by_collection(
|
|
324
|
+
self,
|
|
325
|
+
collection_id: str,
|
|
326
|
+
agents_cache_dir: Optional[Path] = None,
|
|
327
|
+
) -> List[Dict[str, Any]]:
|
|
328
|
+
"""Get all agents from a specific collection.
|
|
329
|
+
|
|
330
|
+
NEW: Enables collection-based agent selection.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
collection_id: Collection identifier (e.g., "bobmatnyc/claude-mpm-agents")
|
|
334
|
+
agents_cache_dir: Directory containing agents cache
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
List of agent dictionaries from the specified collection
|
|
338
|
+
|
|
339
|
+
Example:
|
|
340
|
+
>>> service = MultiSourceAgentDeploymentService()
|
|
341
|
+
>>> agents = service.get_agents_by_collection("bobmatnyc/claude-mpm-agents")
|
|
342
|
+
>>> len(agents)
|
|
343
|
+
45
|
|
344
|
+
"""
|
|
345
|
+
if not agents_cache_dir:
|
|
346
|
+
cache_dir = Path.home() / ".claude-mpm" / "cache"
|
|
347
|
+
agents_cache_dir = cache_dir / "agents"
|
|
348
|
+
|
|
349
|
+
if not agents_cache_dir.exists():
|
|
350
|
+
self.logger.warning(f"Agents cache directory not found: {agents_cache_dir}")
|
|
351
|
+
return []
|
|
352
|
+
|
|
353
|
+
# Use RemoteAgentDiscoveryService to get collection agents
|
|
354
|
+
remote_service = RemoteAgentDiscoveryService(agents_cache_dir)
|
|
355
|
+
collection_agents = remote_service.get_agents_by_collection(collection_id)
|
|
356
|
+
|
|
357
|
+
self.logger.info(
|
|
358
|
+
f"Retrieved {len(collection_agents)} agents from collection '{collection_id}'"
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
return collection_agents
|
|
362
|
+
|
|
192
363
|
def select_highest_version_agents(
|
|
193
364
|
self, agents_by_name: Dict[str, List[Dict[str, Any]]]
|
|
194
365
|
) -> Dict[str, Dict[str, Any]]:
|
|
@@ -297,7 +468,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
297
468
|
system_templates_dir: Optional[Path] = None,
|
|
298
469
|
project_agents_dir: Optional[Path] = None,
|
|
299
470
|
user_agents_dir: Optional[Path] = None,
|
|
300
|
-
|
|
471
|
+
agents_cache_dir: Optional[Path] = None,
|
|
301
472
|
working_directory: Optional[Path] = None,
|
|
302
473
|
excluded_agents: Optional[List[str]] = None,
|
|
303
474
|
config: Optional[Config] = None,
|
|
@@ -309,7 +480,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
309
480
|
system_templates_dir: Directory containing system agent templates
|
|
310
481
|
project_agents_dir: Directory containing project-specific agents
|
|
311
482
|
user_agents_dir: Directory containing user custom agents (DEPRECATED)
|
|
312
|
-
|
|
483
|
+
agents_cache_dir: Directory containing cached agents from Git sources
|
|
313
484
|
working_directory: Current working directory for finding project agents
|
|
314
485
|
excluded_agents: List of agent names to exclude from deployment
|
|
315
486
|
config: Configuration object for additional filtering
|
|
@@ -326,7 +497,7 @@ class MultiSourceAgentDeploymentService:
|
|
|
326
497
|
system_templates_dir=system_templates_dir,
|
|
327
498
|
project_agents_dir=project_agents_dir,
|
|
328
499
|
user_agents_dir=user_agents_dir,
|
|
329
|
-
|
|
500
|
+
agents_cache_dir=agents_cache_dir,
|
|
330
501
|
working_directory=working_directory,
|
|
331
502
|
)
|
|
332
503
|
|
|
@@ -360,10 +531,28 @@ class MultiSourceAgentDeploymentService:
|
|
|
360
531
|
|
|
361
532
|
# Apply exclusion filters
|
|
362
533
|
if excluded_agents:
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
534
|
+
# Find agents to remove by matching name field or agent_id portion of canonical_id
|
|
535
|
+
agents_to_remove = []
|
|
536
|
+
excluded_set = {name.lower() for name in excluded_agents}
|
|
537
|
+
|
|
538
|
+
for canonical_id, agent_info in list(selected_agents.items()):
|
|
539
|
+
# Check agent name field
|
|
540
|
+
agent_name = agent_info.get("name", "").lower()
|
|
541
|
+
|
|
542
|
+
# Also check the agent_id portion of canonical_id (after the colon)
|
|
543
|
+
# Example: "bobmatnyc/claude-mpm-agents:pm" -> "pm"
|
|
544
|
+
agent_id = canonical_id.split(":")[-1].lower() if ":" in canonical_id else canonical_id.lower()
|
|
545
|
+
|
|
546
|
+
if agent_name in excluded_set or agent_id in excluded_set:
|
|
547
|
+
agents_to_remove.append(canonical_id)
|
|
548
|
+
self.logger.info(
|
|
549
|
+
f"Excluding agent '{agent_info.get('name', agent_id)}' "
|
|
550
|
+
f"(canonical_id: {canonical_id}) from deployment"
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
# Remove matched agents
|
|
554
|
+
for canonical_id in agents_to_remove:
|
|
555
|
+
del selected_agents[canonical_id]
|
|
367
556
|
|
|
368
557
|
# Apply config-based filtering if provided
|
|
369
558
|
if config:
|
|
@@ -412,6 +601,105 @@ class MultiSourceAgentDeploymentService:
|
|
|
412
601
|
|
|
413
602
|
return agents_to_deploy, agent_sources, cleanup_results
|
|
414
603
|
|
|
604
|
+
def cleanup_excluded_agents(
|
|
605
|
+
self,
|
|
606
|
+
deployed_agents_dir: Path,
|
|
607
|
+
agents_to_deploy: Dict[str, Path],
|
|
608
|
+
) -> Dict[str, Any]:
|
|
609
|
+
"""Remove agents from deployed directory that aren't in the deployment list.
|
|
610
|
+
|
|
611
|
+
Similar to skill cleanup logic, this removes agents that were previously
|
|
612
|
+
deployed but are no longer in the enabled agents list (e.g., filtered out
|
|
613
|
+
by profile configuration).
|
|
614
|
+
|
|
615
|
+
Args:
|
|
616
|
+
deployed_agents_dir: Directory containing deployed agents (~/.claude/agents)
|
|
617
|
+
agents_to_deploy: Dictionary mapping agent file stems to template paths
|
|
618
|
+
|
|
619
|
+
Returns:
|
|
620
|
+
Dictionary with cleanup results:
|
|
621
|
+
- removed: List of removed agent names
|
|
622
|
+
- errors: List of errors during cleanup
|
|
623
|
+
"""
|
|
624
|
+
cleanup_results = {"removed": [], "errors": []}
|
|
625
|
+
|
|
626
|
+
# Safety check - only operate on deployed agents directory
|
|
627
|
+
if not deployed_agents_dir.exists():
|
|
628
|
+
self.logger.debug("Deployed agents directory does not exist, no cleanup needed")
|
|
629
|
+
return cleanup_results
|
|
630
|
+
|
|
631
|
+
# Build set of agent names that should exist (file stems without .md extension)
|
|
632
|
+
expected_agents = set(agents_to_deploy.keys())
|
|
633
|
+
|
|
634
|
+
try:
|
|
635
|
+
# Check each file in deployed_agents_dir
|
|
636
|
+
for item in deployed_agents_dir.iterdir():
|
|
637
|
+
# Only process .md files
|
|
638
|
+
if not item.is_file() or item.suffix != ".md":
|
|
639
|
+
continue
|
|
640
|
+
|
|
641
|
+
# Skip hidden files
|
|
642
|
+
if item.name.startswith("."):
|
|
643
|
+
continue
|
|
644
|
+
|
|
645
|
+
# Get agent name (file stem)
|
|
646
|
+
agent_name = item.stem
|
|
647
|
+
|
|
648
|
+
# Check if this agent should be kept
|
|
649
|
+
if agent_name not in expected_agents:
|
|
650
|
+
try:
|
|
651
|
+
# Security: Validate path is within deployed_agents_dir
|
|
652
|
+
resolved_item = item.resolve()
|
|
653
|
+
resolved_target = deployed_agents_dir.resolve()
|
|
654
|
+
|
|
655
|
+
if not str(resolved_item).startswith(str(resolved_target)):
|
|
656
|
+
self.logger.error(
|
|
657
|
+
f"Refusing to remove path outside target directory: {item}"
|
|
658
|
+
)
|
|
659
|
+
cleanup_results["errors"].append(
|
|
660
|
+
{
|
|
661
|
+
"agent": agent_name,
|
|
662
|
+
"error": "Path outside target directory",
|
|
663
|
+
}
|
|
664
|
+
)
|
|
665
|
+
continue
|
|
666
|
+
|
|
667
|
+
# Remove the agent file
|
|
668
|
+
item.unlink()
|
|
669
|
+
cleanup_results["removed"].append(agent_name)
|
|
670
|
+
self.logger.info(f"Removed excluded agent: {agent_name}")
|
|
671
|
+
|
|
672
|
+
except PermissionError as e:
|
|
673
|
+
error_msg = f"Permission denied removing {agent_name}: {e}"
|
|
674
|
+
self.logger.error(error_msg)
|
|
675
|
+
cleanup_results["errors"].append(
|
|
676
|
+
{"agent": agent_name, "error": error_msg}
|
|
677
|
+
)
|
|
678
|
+
except Exception as e:
|
|
679
|
+
error_msg = f"Error removing {agent_name}: {e}"
|
|
680
|
+
self.logger.error(error_msg)
|
|
681
|
+
cleanup_results["errors"].append(
|
|
682
|
+
{"agent": agent_name, "error": error_msg}
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
except Exception as e:
|
|
686
|
+
self.logger.error(f"Error during agent cleanup: {e}")
|
|
687
|
+
cleanup_results["errors"].append(
|
|
688
|
+
{"agent": "cleanup_process", "error": str(e)}
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
# Log cleanup summary
|
|
692
|
+
if cleanup_results["removed"]:
|
|
693
|
+
self.logger.info(
|
|
694
|
+
f"Cleanup complete: removed {len(cleanup_results['removed'])} excluded agents"
|
|
695
|
+
)
|
|
696
|
+
if cleanup_results["errors"]:
|
|
697
|
+
self.logger.warning(
|
|
698
|
+
f"Encountered {len(cleanup_results['errors'])} errors during cleanup"
|
|
699
|
+
)
|
|
700
|
+
|
|
701
|
+
return cleanup_results
|
|
702
|
+
|
|
415
703
|
def cleanup_outdated_user_agents(
|
|
416
704
|
self,
|
|
417
705
|
agents_by_name: Dict[str, List[Dict[str, Any]]],
|
|
@@ -720,17 +1008,20 @@ class MultiSourceAgentDeploymentService:
|
|
|
720
1008
|
comparison_results["needs_update"].append(agent_name)
|
|
721
1009
|
continue
|
|
722
1010
|
|
|
723
|
-
# Read template version
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
template_data.get("agent_version")
|
|
729
|
-
or template_data.get("version")
|
|
730
|
-
or metadata.get("version", "0.0.0")
|
|
1011
|
+
# Read template version using format-aware helper
|
|
1012
|
+
version_string = self._read_template_version(template_path)
|
|
1013
|
+
if not version_string:
|
|
1014
|
+
self.logger.warning(
|
|
1015
|
+
f"Could not extract version from template for '{agent_name}', skipping"
|
|
731
1016
|
)
|
|
1017
|
+
continue
|
|
1018
|
+
|
|
1019
|
+
try:
|
|
1020
|
+
template_version = self.version_manager.parse_version(version_string)
|
|
732
1021
|
except Exception as e:
|
|
733
|
-
self.logger.warning(
|
|
1022
|
+
self.logger.warning(
|
|
1023
|
+
f"Error parsing version '{version_string}' for '{agent_name}': {e}"
|
|
1024
|
+
)
|
|
734
1025
|
continue
|
|
735
1026
|
|
|
736
1027
|
# Read deployed version
|