claude-mpm 4.0.17__py3-none-any.whl → 4.0.20__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.
Files changed (46) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__main__.py +4 -0
  3. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +38 -2
  4. claude_mpm/agents/OUTPUT_STYLE.md +84 -0
  5. claude_mpm/agents/templates/qa.json +24 -12
  6. claude_mpm/cli/__init__.py +85 -1
  7. claude_mpm/cli/__main__.py +4 -0
  8. claude_mpm/cli/commands/mcp_install_commands.py +62 -5
  9. claude_mpm/cli/commands/mcp_server_commands.py +60 -79
  10. claude_mpm/cli/commands/memory.py +32 -5
  11. claude_mpm/cli/commands/run.py +33 -6
  12. claude_mpm/cli/parsers/base_parser.py +5 -0
  13. claude_mpm/cli/parsers/run_parser.py +5 -0
  14. claude_mpm/cli/utils.py +17 -4
  15. claude_mpm/core/base_service.py +1 -1
  16. claude_mpm/core/config.py +70 -5
  17. claude_mpm/core/framework_loader.py +342 -31
  18. claude_mpm/core/interactive_session.py +55 -1
  19. claude_mpm/core/oneshot_session.py +7 -1
  20. claude_mpm/core/output_style_manager.py +468 -0
  21. claude_mpm/core/unified_paths.py +190 -21
  22. claude_mpm/hooks/claude_hooks/hook_handler.py +91 -16
  23. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +3 -0
  24. claude_mpm/init.py +1 -0
  25. claude_mpm/scripts/mcp_server.py +68 -0
  26. claude_mpm/scripts/mcp_wrapper.py +39 -0
  27. claude_mpm/services/agents/deployment/agent_deployment.py +151 -7
  28. claude_mpm/services/agents/deployment/agent_template_builder.py +37 -1
  29. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +441 -0
  30. claude_mpm/services/agents/memory/__init__.py +0 -2
  31. claude_mpm/services/agents/memory/agent_memory_manager.py +737 -43
  32. claude_mpm/services/agents/memory/content_manager.py +144 -14
  33. claude_mpm/services/agents/memory/template_generator.py +7 -354
  34. claude_mpm/services/mcp_gateway/core/singleton_manager.py +312 -0
  35. claude_mpm/services/mcp_gateway/core/startup_verification.py +315 -0
  36. claude_mpm/services/mcp_gateway/main.py +7 -0
  37. claude_mpm/services/mcp_gateway/server/stdio_server.py +184 -176
  38. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +453 -0
  39. claude_mpm/services/subprocess_launcher_service.py +5 -0
  40. {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/METADATA +1 -1
  41. {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/RECORD +45 -38
  42. {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/entry_points.txt +1 -0
  43. claude_mpm/services/agents/memory/analyzer.py +0 -430
  44. {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/WHEEL +0 -0
  45. {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/licenses/LICENSE +0 -0
  46. {claude_mpm-4.0.17.dist-info → claude_mpm-4.0.20.dist-info}/top_level.txt +0 -0
@@ -32,7 +32,7 @@ import os
32
32
  import shutil
33
33
  import time
34
34
  from pathlib import Path
35
- from typing import Any, Dict, List, Optional
35
+ from typing import Any, Dict, List, Optional, Tuple
36
36
 
37
37
  from claude_mpm.config.paths import paths
38
38
  from claude_mpm.constants import AgentMetadata, EnvironmentVars, Paths
@@ -55,6 +55,7 @@ from .agent_metrics_collector import AgentMetricsCollector
55
55
  from .agent_template_builder import AgentTemplateBuilder
56
56
  from .agent_validator import AgentValidator
57
57
  from .agent_version_manager import AgentVersionManager
58
+ from .multi_source_deployment_service import MultiSourceAgentDeploymentService
58
59
 
59
60
 
60
61
  class AgentDeploymentService(AgentDeploymentInterface):
@@ -161,6 +162,9 @@ class AgentDeploymentService(AgentDeploymentInterface):
161
162
 
162
163
  # Initialize discovery service (after templates_dir is set)
163
164
  self.discovery_service = AgentDiscoveryService(self.templates_dir)
165
+
166
+ # Initialize multi-source deployment service for version comparison
167
+ self.multi_source_service = MultiSourceAgentDeploymentService()
164
168
 
165
169
  # Find base agent file
166
170
  if base_agent_path:
@@ -310,20 +314,40 @@ class AgentDeploymentService(AgentDeploymentInterface):
310
314
  # Load base agent content
311
315
  base_agent_data, base_agent_version = self._load_base_agent()
312
316
 
313
- # Get and filter template files
314
- template_files = self._get_filtered_templates(excluded_agents, config)
315
- results["total"] = len(template_files)
317
+ # Check if we should use multi-source deployment
318
+ use_multi_source = self._should_use_multi_source_deployment(deployment_mode)
319
+
320
+ if use_multi_source:
321
+ # Use multi-source deployment to get highest version agents
322
+ template_files, agent_sources = self._get_multi_source_templates(
323
+ excluded_agents, config, agents_dir
324
+ )
325
+ results["total"] = len(template_files)
326
+ results["multi_source"] = True
327
+ results["agent_sources"] = agent_sources
328
+ else:
329
+ # Get and filter template files from single source
330
+ template_files = self._get_filtered_templates(excluded_agents, config)
331
+ results["total"] = len(template_files)
332
+ agent_sources = {}
316
333
 
317
334
  # Deploy each agent template
318
335
  for template_file in template_files:
336
+ template_file_path = template_file if isinstance(template_file, Path) else Path(template_file)
337
+ agent_name = template_file_path.stem
338
+
339
+ # Get source info for this agent (agent_sources now uses file stems as keys)
340
+ source_info = agent_sources.get(agent_name, "unknown") if agent_sources else "single"
341
+
319
342
  self._deploy_single_agent(
320
- template_file=template_file,
343
+ template_file=template_file_path,
321
344
  agents_dir=agents_dir,
322
345
  base_agent_data=base_agent_data,
323
346
  base_agent_version=base_agent_version,
324
347
  force_rebuild=force_rebuild,
325
348
  deployment_mode=deployment_mode,
326
349
  results=results,
350
+ source_info=source_info,
327
351
  )
328
352
 
329
353
  # Deploy system instructions and framework files
@@ -467,8 +491,10 @@ class AgentDeploymentService(AgentDeploymentInterface):
467
491
  self.logger.warning(f"Could not load base agent: {e}")
468
492
 
469
493
  # Build the agent markdown
494
+ # For single agent deployment, determine source from template location
495
+ source_info = self._determine_agent_source(template_file)
470
496
  agent_content = self.template_builder.build_agent_markdown(
471
- agent_name, template_file, base_agent_data
497
+ agent_name, template_file, base_agent_data, source_info
472
498
  )
473
499
  if not agent_content:
474
500
  self.logger.error(f"Failed to build agent content for {agent_name}")
@@ -718,6 +744,7 @@ class AgentDeploymentService(AgentDeploymentInterface):
718
744
  force_rebuild: bool,
719
745
  deployment_mode: str,
720
746
  results: Dict[str, Any],
747
+ source_info: str = "unknown",
721
748
  ) -> None:
722
749
  """
723
750
  Deploy a single agent template.
@@ -733,6 +760,7 @@ class AgentDeploymentService(AgentDeploymentInterface):
733
760
  force_rebuild: Whether to force rebuild
734
761
  deployment_mode: Deployment mode (update/project)
735
762
  results: Results dictionary to update
763
+ source_info: Source of the agent (system/project/user)
736
764
  """
737
765
  try:
738
766
  # METRICS: Track individual agent deployment time
@@ -762,7 +790,7 @@ class AgentDeploymentService(AgentDeploymentInterface):
762
790
 
763
791
  # Build the agent file as markdown with YAML frontmatter
764
792
  agent_content = self.template_builder.build_agent_markdown(
765
- agent_name, template_file, base_agent_data
793
+ agent_name, template_file, base_agent_data, source_info
766
794
  )
767
795
 
768
796
  # Write the agent file
@@ -912,6 +940,122 @@ class AgentDeploymentService(AgentDeploymentInterface):
912
940
 
913
941
  validator = AgentFrontmatterValidator(self.logger)
914
942
  return validator.validate_and_repair_existing_agents(agents_dir)
943
+
944
+ def _determine_agent_source(self, template_path: Path) -> str:
945
+ """Determine the source of an agent from its template path.
946
+
947
+ WHY: When deploying single agents, we need to track their source
948
+ for proper version management and debugging.
949
+
950
+ Args:
951
+ template_path: Path to the agent template
952
+
953
+ Returns:
954
+ Source string (system/project/user/unknown)
955
+ """
956
+ template_str = str(template_path.resolve())
957
+
958
+ # Check if it's a system template
959
+ if "/claude_mpm/agents/templates/" in template_str or "/src/claude_mpm/agents/templates/" in template_str:
960
+ return "system"
961
+
962
+ # Check if it's a project agent
963
+ if "/.claude-mpm/agents/" in template_str:
964
+ # Check if it's in the current working directory
965
+ if str(self.working_directory) in template_str:
966
+ return "project"
967
+ # Check if it's in user home
968
+ elif str(Path.home()) in template_str:
969
+ return "user"
970
+
971
+ return "unknown"
972
+
973
+ def _should_use_multi_source_deployment(self, deployment_mode: str) -> bool:
974
+ """Determine if multi-source deployment should be used.
975
+
976
+ WHY: Multi-source deployment ensures the highest version wins,
977
+ but we may want to preserve backward compatibility in some modes.
978
+
979
+ Args:
980
+ deployment_mode: Current deployment mode
981
+
982
+ Returns:
983
+ True if multi-source deployment should be used
984
+ """
985
+ # Always use multi-source for update mode to get highest versions
986
+ if deployment_mode == "update":
987
+ return True
988
+
989
+ # For project mode, also use multi-source to ensure highest version wins
990
+ # This is the key change - project mode should also compare versions
991
+ if deployment_mode == "project":
992
+ return True
993
+
994
+ return False
995
+
996
+ def _get_multi_source_templates(
997
+ self, excluded_agents: List[str], config: Config, agents_dir: Path
998
+ ) -> Tuple[List[Path], Dict[str, str]]:
999
+ """Get agent templates from multiple sources with version comparison.
1000
+
1001
+ WHY: This method uses the multi-source service to discover agents
1002
+ from all available sources and select the highest version of each.
1003
+
1004
+ Args:
1005
+ excluded_agents: List of agents to exclude
1006
+ config: Configuration object
1007
+ agents_dir: Target deployment directory
1008
+
1009
+ Returns:
1010
+ Tuple of (template_files, agent_sources)
1011
+ """
1012
+ # Determine source directories
1013
+ system_templates_dir = self.templates_dir
1014
+ project_agents_dir = None
1015
+ user_agents_dir = None
1016
+
1017
+ # Check for project agents
1018
+ if self.working_directory:
1019
+ potential_project_dir = self.working_directory / ".claude-mpm" / "agents"
1020
+ if potential_project_dir.exists():
1021
+ project_agents_dir = potential_project_dir
1022
+ self.logger.info(f"Found project agents at: {project_agents_dir}")
1023
+
1024
+ # Check for user agents
1025
+ user_home = Path.home()
1026
+ potential_user_dir = user_home / ".claude-mpm" / "agents"
1027
+ if potential_user_dir.exists():
1028
+ user_agents_dir = potential_user_dir
1029
+ self.logger.info(f"Found user agents at: {user_agents_dir}")
1030
+
1031
+ # Get agents with version comparison
1032
+ agents_to_deploy, agent_sources = self.multi_source_service.get_agents_for_deployment(
1033
+ system_templates_dir=system_templates_dir,
1034
+ project_agents_dir=project_agents_dir,
1035
+ user_agents_dir=user_agents_dir,
1036
+ working_directory=self.working_directory,
1037
+ excluded_agents=excluded_agents,
1038
+ config=config
1039
+ )
1040
+
1041
+ # Compare with deployed versions if agents directory exists
1042
+ if agents_dir.exists():
1043
+ comparison_results = self.multi_source_service.compare_deployed_versions(
1044
+ deployed_agents_dir=agents_dir,
1045
+ agents_to_deploy=agents_to_deploy,
1046
+ agent_sources=agent_sources
1047
+ )
1048
+
1049
+ # Log version upgrades and source changes
1050
+ if comparison_results.get("version_upgrades"):
1051
+ self.logger.info(f"Version upgrades available for {len(comparison_results['version_upgrades'])} agents")
1052
+ if comparison_results.get("source_changes"):
1053
+ self.logger.info(f"Source changes for {len(comparison_results['source_changes'])} agents")
1054
+
1055
+ # Convert to list of Path objects
1056
+ template_files = list(agents_to_deploy.values())
1057
+
1058
+ return template_files, agent_sources
915
1059
 
916
1060
  # ================================================================================
917
1061
  # Interface Adapter Methods
@@ -31,7 +31,7 @@ class AgentTemplateBuilder:
31
31
  self.logger = get_logger(__name__)
32
32
 
33
33
  def build_agent_markdown(
34
- self, agent_name: str, template_path: Path, base_agent_data: dict
34
+ self, agent_name: str, template_path: Path, base_agent_data: dict, source_info: str = "unknown"
35
35
  ) -> str:
36
36
  """
37
37
  Build a complete agent markdown file with YAML frontmatter.
@@ -40,6 +40,7 @@ class AgentTemplateBuilder:
40
40
  agent_name: Name of the agent
41
41
  template_path: Path to the agent template JSON file
42
42
  base_agent_data: Base agent configuration data
43
+ source_info: Source of the agent (system/project/user)
43
44
 
44
45
  Returns:
45
46
  Complete markdown content with YAML frontmatter
@@ -197,6 +198,8 @@ class AgentTemplateBuilder:
197
198
  f"color: {color}",
198
199
  f"version: {agent_version}",
199
200
  f"type: {agent_type}",
201
+ f"source: {source_info}", # Track which source provided this agent
202
+ "author: claude-mpm", # Mark as system-managed agent
200
203
  "---",
201
204
  "",
202
205
  ]
@@ -211,6 +214,39 @@ class AgentTemplateBuilder:
211
214
  or base_agent_data.get("instructions")
212
215
  or "# Agent Instructions\n\nThis agent provides specialized assistance."
213
216
  )
217
+
218
+ # Add memory update instructions if not already present
219
+ if "memory-update" not in content and "Remember" not in content:
220
+ memory_instructions = """
221
+
222
+ ## Memory Updates
223
+
224
+ When you learn something important about this project that would be useful for future tasks, include it in your response JSON block:
225
+
226
+ ```json
227
+ {
228
+ "memory-update": {
229
+ "Project Architecture": ["Key architectural patterns or structures"],
230
+ "Implementation Guidelines": ["Important coding standards or practices"],
231
+ "Current Technical Context": ["Project-specific technical details"]
232
+ }
233
+ }
234
+ ```
235
+
236
+ Or use the simpler "remember" field for general learnings:
237
+
238
+ ```json
239
+ {
240
+ "remember": ["Learning 1", "Learning 2"]
241
+ }
242
+ ```
243
+
244
+ Only include memories that are:
245
+ - Project-specific (not generic programming knowledge)
246
+ - Likely to be useful in future tasks
247
+ - Not already documented elsewhere
248
+ """
249
+ content = content + memory_instructions
214
250
 
215
251
  return frontmatter + content
216
252