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.

Files changed (263) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/BASE_AGENT.md +164 -0
  4. claude_mpm/agents/{PM_INSTRUCTIONS_TEACH.md → CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md} +721 -41
  5. claude_mpm/agents/MEMORY.md +1 -1
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +468 -468
  7. claude_mpm/agents/WORKFLOW.md +5 -254
  8. claude_mpm/agents/agent_loader.py +13 -44
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/frontmatter_validator.py +70 -2
  11. claude_mpm/agents/templates/circuit-breakers.md +431 -45
  12. claude_mpm/cli/__init__.py +0 -1
  13. claude_mpm/cli/__main__.py +4 -0
  14. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  15. claude_mpm/cli/commands/agent_state_manager.py +18 -27
  16. claude_mpm/cli/commands/agents.py +175 -37
  17. claude_mpm/cli/commands/auto_configure.py +723 -236
  18. claude_mpm/cli/commands/config.py +88 -2
  19. claude_mpm/cli/commands/configure.py +1262 -157
  20. claude_mpm/cli/commands/configure_agent_display.py +25 -6
  21. claude_mpm/cli/commands/mpm_init/core.py +225 -46
  22. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  23. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  24. claude_mpm/cli/commands/postmortem.py +1 -1
  25. claude_mpm/cli/commands/profile.py +277 -0
  26. claude_mpm/cli/commands/skills.py +214 -189
  27. claude_mpm/cli/commands/summarize.py +413 -0
  28. claude_mpm/cli/executor.py +21 -3
  29. claude_mpm/cli/interactive/agent_wizard.py +85 -10
  30. claude_mpm/cli/parsers/agents_parser.py +54 -9
  31. claude_mpm/cli/parsers/auto_configure_parser.py +13 -138
  32. claude_mpm/cli/parsers/base_parser.py +12 -0
  33. claude_mpm/cli/parsers/config_parser.py +153 -83
  34. claude_mpm/cli/parsers/profile_parser.py +148 -0
  35. claude_mpm/cli/parsers/skills_parser.py +3 -2
  36. claude_mpm/cli/startup.py +879 -149
  37. claude_mpm/commands/mpm-config.md +28 -0
  38. claude_mpm/commands/mpm-doctor.md +9 -22
  39. claude_mpm/commands/mpm-help.md +5 -287
  40. claude_mpm/commands/mpm-init.md +81 -507
  41. claude_mpm/commands/mpm-monitor.md +15 -402
  42. claude_mpm/commands/mpm-organize.md +120 -0
  43. claude_mpm/commands/mpm-postmortem.md +6 -108
  44. claude_mpm/commands/mpm-session-resume.md +12 -363
  45. claude_mpm/commands/mpm-status.md +5 -69
  46. claude_mpm/commands/mpm-ticket-view.md +52 -495
  47. claude_mpm/commands/mpm-version.md +5 -107
  48. claude_mpm/config/agent_sources.py +27 -0
  49. claude_mpm/core/config.py +2 -4
  50. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  51. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  52. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  53. claude_mpm/core/framework_loader.py +4 -2
  54. claude_mpm/core/logger.py +13 -0
  55. claude_mpm/core/optimized_startup.py +59 -0
  56. claude_mpm/core/output_style_manager.py +173 -43
  57. claude_mpm/core/shared/config_loader.py +1 -1
  58. claude_mpm/core/socketio_pool.py +3 -3
  59. claude_mpm/core/unified_agent_registry.py +134 -16
  60. claude_mpm/core/unified_config.py +22 -0
  61. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  62. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  63. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  64. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  65. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  66. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  67. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  68. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  69. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  70. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  71. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  72. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  73. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  74. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  75. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  76. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  77. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  78. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  79. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  85. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  86. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  87. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  88. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  89. claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
  90. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  91. claude_mpm/hooks/claude_hooks/memory_integration.py +28 -0
  92. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  93. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  94. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  95. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  96. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  97. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  98. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  99. claude_mpm/hooks/claude_hooks/services/connection_manager.py +30 -6
  100. claude_mpm/hooks/memory_integration_hook.py +46 -1
  101. claude_mpm/init.py +63 -19
  102. claude_mpm/models/agent_definition.py +7 -0
  103. claude_mpm/models/git_repository.py +3 -3
  104. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  105. claude_mpm/scripts/launch_monitor.py +93 -13
  106. claude_mpm/scripts/start_activity_logging.py +0 -0
  107. claude_mpm/services/agents/agent_builder.py +3 -3
  108. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  109. claude_mpm/services/agents/agent_review_service.py +280 -0
  110. claude_mpm/services/agents/cache_git_manager.py +6 -6
  111. claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
  112. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -5
  113. claude_mpm/services/agents/deployment/agent_template_builder.py +5 -3
  114. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  115. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +320 -29
  116. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +546 -68
  117. claude_mpm/services/agents/git_source_manager.py +36 -2
  118. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  119. claude_mpm/services/agents/recommender.py +5 -3
  120. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  121. claude_mpm/services/agents/sources/git_source_sync_service.py +13 -6
  122. claude_mpm/services/agents/startup_sync.py +22 -2
  123. claude_mpm/services/agents/toolchain_detector.py +10 -6
  124. claude_mpm/services/analysis/__init__.py +11 -1
  125. claude_mpm/services/analysis/clone_detector.py +1030 -0
  126. claude_mpm/services/command_deployment_service.py +81 -10
  127. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  128. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  129. claude_mpm/services/event_bus/config.py +3 -1
  130. claude_mpm/services/git/git_operations_service.py +101 -16
  131. claude_mpm/services/monitor/daemon.py +9 -2
  132. claude_mpm/services/monitor/daemon_manager.py +39 -3
  133. claude_mpm/services/monitor/management/lifecycle.py +8 -1
  134. claude_mpm/services/monitor/server.py +698 -22
  135. claude_mpm/services/pm_skills_deployer.py +676 -0
  136. claude_mpm/services/profile_manager.py +331 -0
  137. claude_mpm/services/project/project_organizer.py +4 -0
  138. claude_mpm/services/self_upgrade_service.py +120 -12
  139. claude_mpm/services/skills/__init__.py +3 -0
  140. claude_mpm/services/skills/git_skill_source_manager.py +130 -2
  141. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  142. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  143. claude_mpm/services/skills_deployer.py +126 -9
  144. claude_mpm/services/socketio/dashboard_server.py +1 -0
  145. claude_mpm/services/socketio/event_normalizer.py +51 -6
  146. claude_mpm/services/socketio/server/core.py +386 -108
  147. claude_mpm/services/version_control/git_operations.py +103 -0
  148. claude_mpm/skills/skill_manager.py +92 -3
  149. claude_mpm/utils/agent_dependency_loader.py +14 -2
  150. claude_mpm/utils/agent_filters.py +17 -44
  151. claude_mpm/utils/gitignore.py +3 -0
  152. claude_mpm/utils/migration.py +4 -4
  153. claude_mpm/utils/robust_installer.py +47 -3
  154. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/METADATA +57 -87
  155. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/RECORD +160 -211
  156. claude_mpm-5.4.41.dist-info/entry_points.txt +5 -0
  157. claude_mpm-5.4.41.dist-info/licenses/LICENSE +94 -0
  158. claude_mpm-5.4.41.dist-info/licenses/LICENSE-FAQ.md +153 -0
  159. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  160. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  161. claude_mpm/agents/BASE_OPS.md +0 -219
  162. claude_mpm/agents/BASE_PM.md +0 -480
  163. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  164. claude_mpm/agents/BASE_QA.md +0 -167
  165. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  166. claude_mpm/agents/base_agent_loader.py +0 -601
  167. claude_mpm/cli/commands/agents_detect.py +0 -380
  168. claude_mpm/cli/commands/agents_recommend.py +0 -309
  169. claude_mpm/cli/ticket_cli.py +0 -35
  170. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  171. claude_mpm/commands/mpm-agents-detect.md +0 -177
  172. claude_mpm/commands/mpm-agents-list.md +0 -131
  173. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  174. claude_mpm/commands/mpm-config-view.md +0 -150
  175. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  176. claude_mpm/dashboard/analysis_runner.py +0 -455
  177. claude_mpm/dashboard/index.html +0 -13
  178. claude_mpm/dashboard/open_dashboard.py +0 -66
  179. claude_mpm/dashboard/static/css/activity.css +0 -1958
  180. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  181. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  182. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  183. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  184. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  185. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  186. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  187. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  188. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  189. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  190. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  191. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  192. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  193. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  194. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  195. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  196. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  197. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  198. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  199. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  200. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  201. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  202. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  203. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  204. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  205. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  206. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  207. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  208. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  209. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  210. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  211. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  212. claude_mpm/dashboard/templates/code_simple.html +0 -153
  213. claude_mpm/dashboard/templates/index.html +0 -606
  214. claude_mpm/dashboard/test_dashboard.html +0 -372
  215. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  216. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  217. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  218. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  219. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  220. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  221. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  222. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  223. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  224. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  225. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  226. claude_mpm/scripts/mcp_server.py +0 -75
  227. claude_mpm/scripts/mcp_wrapper.py +0 -39
  228. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  229. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  230. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  231. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  232. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  233. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  234. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  235. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  236. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  237. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  238. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  239. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  240. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  241. claude_mpm/services/mcp_gateway/main.py +0 -589
  242. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  243. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  244. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  245. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  246. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  247. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  248. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  249. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  250. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  251. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  252. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  253. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  254. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  255. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  256. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  257. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  258. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  259. claude_mpm-5.0.9.dist-info/entry_points.txt +0 -10
  260. claude_mpm-5.0.9.dist-info/licenses/LICENSE +0 -21
  261. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  262. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/WHEEL +0 -0
  263. {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
- remote_agents_dir: Optional[Path] = None,
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, remote, project).
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. Remote agents - GitHub-synced agents from cache
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
- remote_agents_dir: Directory containing cached remote agents
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 remote_agents_dir:
106
- # Check for remote agents in cache directory
228
+ if not agents_cache_dir:
229
+ # Check for agents in cache directory
107
230
  cache_dir = Path.home() / ".claude-mpm" / "cache"
108
- remote_agents_dir = cache_dir / "remote-agents"
109
- if not remote_agents_dir.exists():
110
- remote_agents_dir = None
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", remote_agents_dir),
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 agent_name not in agents_by_name:
161
- agents_by_name[agent_name] = []
291
+ if matching_key not in agents_by_name:
292
+ agents_by_name[matching_key] = []
162
293
 
163
- agents_by_name[agent_name].append(agent_info)
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
- remote_agents_dir: Optional[Path] = None,
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
- remote_agents_dir: Directory containing cached remote agents
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
- remote_agents_dir=remote_agents_dir,
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
- for agent_name in excluded_agents:
364
- if agent_name in selected_agents:
365
- self.logger.info(f"Excluding agent '{agent_name}' from deployment")
366
- del selected_agents[agent_name]
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
- try:
725
- template_data = json.loads(template_path.read_text())
726
- metadata = template_data.get("metadata", {})
727
- template_version = self.version_manager.parse_version(
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(f"Error reading template for '{agent_name}': {e}")
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