claude-mpm 4.0.19__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/__main__.py +4 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +38 -2
- claude_mpm/agents/OUTPUT_STYLE.md +84 -0
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/cli/__init__.py +23 -1
- claude_mpm/cli/__main__.py +4 -0
- claude_mpm/cli/commands/memory.py +32 -5
- claude_mpm/cli/commands/run.py +33 -6
- claude_mpm/cli/parsers/base_parser.py +5 -0
- claude_mpm/cli/parsers/run_parser.py +5 -0
- claude_mpm/cli/utils.py +17 -4
- claude_mpm/core/base_service.py +1 -1
- claude_mpm/core/config.py +70 -5
- claude_mpm/core/framework_loader.py +342 -31
- claude_mpm/core/interactive_session.py +55 -1
- claude_mpm/core/oneshot_session.py +7 -1
- claude_mpm/core/output_style_manager.py +468 -0
- claude_mpm/core/unified_paths.py +190 -21
- claude_mpm/hooks/claude_hooks/hook_handler.py +91 -16
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +3 -0
- claude_mpm/init.py +1 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +151 -7
- claude_mpm/services/agents/deployment/agent_template_builder.py +37 -1
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +441 -0
- claude_mpm/services/agents/memory/__init__.py +0 -2
- claude_mpm/services/agents/memory/agent_memory_manager.py +737 -43
- claude_mpm/services/agents/memory/content_manager.py +144 -14
- claude_mpm/services/agents/memory/template_generator.py +7 -354
- claude_mpm/services/mcp_gateway/server/stdio_server.py +61 -169
- claude_mpm/services/subprocess_launcher_service.py +5 -0
- {claude_mpm-4.0.19.dist-info → claude_mpm-4.0.20.dist-info}/METADATA +1 -1
- {claude_mpm-4.0.19.dist-info → claude_mpm-4.0.20.dist-info}/RECORD +37 -35
- claude_mpm/services/agents/memory/analyzer.py +0 -430
- {claude_mpm-4.0.19.dist-info → claude_mpm-4.0.20.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.19.dist-info → claude_mpm-4.0.20.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.0.19.dist-info → claude_mpm-4.0.20.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.19.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
|
-
#
|
|
314
|
-
|
|
315
|
-
|
|
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=
|
|
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
|
|