claude-mpm 4.4.0__py3-none-any.whl → 4.4.4__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/agents/WORKFLOW.md +2 -14
- claude_mpm/agents/agent_loader.py +3 -2
- claude_mpm/agents/agent_loader_integration.py +2 -1
- claude_mpm/agents/async_agent_loader.py +2 -2
- claude_mpm/agents/base_agent_loader.py +2 -2
- claude_mpm/agents/frontmatter_validator.py +1 -0
- claude_mpm/agents/system_agent_config.py +2 -1
- claude_mpm/cli/commands/configure.py +2 -29
- claude_mpm/cli/commands/doctor.py +44 -5
- claude_mpm/cli/commands/mpm_init.py +117 -63
- claude_mpm/cli/parsers/configure_parser.py +6 -15
- claude_mpm/cli/startup_logging.py +1 -3
- claude_mpm/config/agent_config.py +1 -1
- claude_mpm/config/paths.py +2 -1
- claude_mpm/core/agent_name_normalizer.py +1 -0
- claude_mpm/core/config.py +2 -1
- claude_mpm/core/config_aliases.py +2 -1
- claude_mpm/core/file_utils.py +0 -1
- claude_mpm/core/framework/__init__.py +38 -0
- claude_mpm/core/framework/formatters/__init__.py +11 -0
- claude_mpm/core/framework/formatters/capability_generator.py +367 -0
- claude_mpm/core/framework/formatters/content_formatter.py +288 -0
- claude_mpm/core/framework/formatters/context_generator.py +184 -0
- claude_mpm/core/framework/loaders/__init__.py +13 -0
- claude_mpm/core/framework/loaders/agent_loader.py +206 -0
- claude_mpm/core/framework/loaders/file_loader.py +223 -0
- claude_mpm/core/framework/loaders/instruction_loader.py +161 -0
- claude_mpm/core/framework/loaders/packaged_loader.py +232 -0
- claude_mpm/core/framework/processors/__init__.py +11 -0
- claude_mpm/core/framework/processors/memory_processor.py +230 -0
- claude_mpm/core/framework/processors/metadata_processor.py +146 -0
- claude_mpm/core/framework/processors/template_processor.py +244 -0
- claude_mpm/core/framework_loader.py +298 -1795
- claude_mpm/core/log_manager.py +2 -1
- claude_mpm/core/tool_access_control.py +1 -0
- claude_mpm/core/unified_agent_registry.py +2 -1
- claude_mpm/core/unified_paths.py +1 -0
- claude_mpm/experimental/cli_enhancements.py +1 -0
- claude_mpm/hooks/__init__.py +9 -1
- claude_mpm/hooks/base_hook.py +1 -0
- claude_mpm/hooks/instruction_reinforcement.py +1 -0
- claude_mpm/hooks/kuzu_memory_hook.py +359 -0
- claude_mpm/hooks/validation_hooks.py +1 -1
- claude_mpm/scripts/mpm_doctor.py +1 -0
- claude_mpm/services/agents/loading/agent_profile_loader.py +1 -1
- claude_mpm/services/agents/loading/base_agent_manager.py +1 -1
- claude_mpm/services/agents/loading/framework_agent_loader.py +1 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -0
- claude_mpm/services/agents/management/agent_management_service.py +1 -1
- claude_mpm/services/agents/memory/memory_categorization_service.py +0 -1
- claude_mpm/services/agents/memory/memory_file_service.py +6 -2
- claude_mpm/services/agents/memory/memory_format_service.py +0 -1
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
- claude_mpm/services/async_session_logger.py +1 -1
- claude_mpm/services/claude_session_logger.py +1 -0
- claude_mpm/services/core/path_resolver.py +2 -0
- claude_mpm/services/diagnostics/checks/__init__.py +2 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +126 -25
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +399 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +4 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +259 -32
- claude_mpm/services/event_bus/direct_relay.py +2 -1
- claude_mpm/services/event_bus/event_bus.py +1 -0
- claude_mpm/services/event_bus/relay.py +3 -2
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +1 -1
- claude_mpm/services/infrastructure/daemon_manager.py +1 -1
- claude_mpm/services/mcp_config_manager.py +67 -4
- claude_mpm/services/mcp_gateway/core/process_pool.py +320 -0
- claude_mpm/services/mcp_gateway/core/startup_verification.py +2 -2
- claude_mpm/services/mcp_gateway/main.py +3 -13
- claude_mpm/services/mcp_gateway/server/stdio_server.py +4 -10
- claude_mpm/services/mcp_gateway/tools/__init__.py +14 -2
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +38 -6
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +527 -0
- claude_mpm/services/memory/cache/simple_cache.py +1 -1
- claude_mpm/services/project/archive_manager.py +159 -96
- claude_mpm/services/project/documentation_manager.py +64 -45
- claude_mpm/services/project/enhanced_analyzer.py +132 -89
- claude_mpm/services/project/project_organizer.py +225 -131
- claude_mpm/services/response_tracker.py +1 -1
- claude_mpm/services/shared/__init__.py +2 -1
- claude_mpm/services/shared/service_factory.py +8 -5
- claude_mpm/services/socketio/server/eventbus_integration.py +1 -1
- claude_mpm/services/unified/__init__.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/__init__.py +3 -3
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +97 -53
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +81 -40
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +277 -178
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +196 -112
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +83 -49
- claude_mpm/services/unified/config_strategies/__init__.py +175 -0
- claude_mpm/services/unified/config_strategies/config_schema.py +735 -0
- claude_mpm/services/unified/config_strategies/context_strategy.py +750 -0
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +1009 -0
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +879 -0
- claude_mpm/services/unified/config_strategies/unified_config_service.py +814 -0
- claude_mpm/services/unified/config_strategies/validation_strategy.py +1144 -0
- claude_mpm/services/unified/deployment_strategies/__init__.py +7 -7
- claude_mpm/services/unified/deployment_strategies/base.py +24 -28
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +168 -88
- claude_mpm/services/unified/deployment_strategies/local.py +49 -34
- claude_mpm/services/unified/deployment_strategies/utils.py +39 -43
- claude_mpm/services/unified/deployment_strategies/vercel.py +30 -24
- claude_mpm/services/unified/interfaces.py +0 -26
- claude_mpm/services/unified/migration.py +17 -40
- claude_mpm/services/unified/strategies.py +9 -26
- claude_mpm/services/unified/unified_analyzer.py +48 -44
- claude_mpm/services/unified/unified_config.py +21 -19
- claude_mpm/services/unified/unified_deployment.py +21 -26
- claude_mpm/storage/state_storage.py +1 -0
- claude_mpm/utils/agent_dependency_loader.py +18 -6
- claude_mpm/utils/common.py +14 -12
- claude_mpm/utils/database_connector.py +15 -12
- claude_mpm/utils/error_handler.py +1 -0
- claude_mpm/utils/log_cleanup.py +1 -0
- claude_mpm/utils/path_operations.py +1 -0
- claude_mpm/utils/session_logging.py +1 -1
- claude_mpm/utils/subprocess_utils.py +1 -0
- claude_mpm/validation/agent_validator.py +1 -1
- {claude_mpm-4.4.0.dist-info → claude_mpm-4.4.4.dist-info}/METADATA +23 -17
- {claude_mpm-4.4.0.dist-info → claude_mpm-4.4.4.dist-info}/RECORD +126 -105
- claude_mpm/cli/commands/configure_tui.py +0 -1927
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +0 -645
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +0 -602
- {claude_mpm-4.4.0.dist-info → claude_mpm-4.4.4.dist-info}/WHEEL +0 -0
- {claude_mpm-4.4.0.dist-info → claude_mpm-4.4.4.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.4.0.dist-info → claude_mpm-4.4.4.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.4.0.dist-info → claude_mpm-4.4.4.dist-info}/top_level.txt +0 -0
@@ -25,11 +25,11 @@ def add_configure_subparser(subparsers) -> argparse.ArgumentParser:
|
|
25
25
|
Returns:
|
26
26
|
The configured configure subparser
|
27
27
|
"""
|
28
|
-
# Configure command - interactive
|
28
|
+
# Configure command - interactive configuration
|
29
29
|
configure_parser = subparsers.add_parser(
|
30
30
|
CLICommands.CONFIGURE.value,
|
31
|
-
help="Interactive
|
32
|
-
description="Launch an interactive Rich-based
|
31
|
+
help="Interactive configuration interface for managing agents and behaviors",
|
32
|
+
description="Launch an interactive Rich-based menu for configuring claude-mpm agents, templates, and behavior files",
|
33
33
|
)
|
34
34
|
|
35
35
|
# Add common arguments
|
@@ -122,21 +122,12 @@ def add_configure_subparser(subparsers) -> argparse.ArgumentParser:
|
|
122
122
|
# Display options
|
123
123
|
display_group = configure_parser.add_argument_group("display options")
|
124
124
|
display_group.add_argument(
|
125
|
-
"--no-colors",
|
126
|
-
)
|
127
|
-
display_group.add_argument(
|
128
|
-
"--compact", action="store_true", help="Use compact display mode"
|
129
|
-
)
|
130
|
-
display_group.add_argument(
|
131
|
-
"--force-rich",
|
125
|
+
"--no-colors",
|
132
126
|
action="store_true",
|
133
|
-
help="
|
127
|
+
help="Disable colored output in the interface",
|
134
128
|
)
|
135
129
|
display_group.add_argument(
|
136
|
-
"--
|
137
|
-
dest="use_textual",
|
138
|
-
action="store_false",
|
139
|
-
help="Disable Textual TUI and use Rich menu interface",
|
130
|
+
"--compact", action="store_true", help="Use compact display mode"
|
140
131
|
)
|
141
132
|
|
142
133
|
return configure_parser
|
@@ -687,9 +687,7 @@ async def trigger_vector_search_indexing(project_root: Optional[Path] = None) ->
|
|
687
687
|
|
688
688
|
# Store PID for logging
|
689
689
|
pid = process.pid
|
690
|
-
logger.debug(
|
691
|
-
f"MCP Vector Search: Indexing process started (PID: {pid})"
|
692
|
-
)
|
690
|
+
logger.debug(f"MCP Vector Search: Indexing process started (PID: {pid})")
|
693
691
|
|
694
692
|
# Don't wait for completion - let it run independently in the background
|
695
693
|
# We don't need to track its completion, so we can safely detach
|
@@ -20,10 +20,10 @@ from enum import Enum
|
|
20
20
|
from pathlib import Path
|
21
21
|
from typing import Any, Dict, List, Optional
|
22
22
|
|
23
|
+
from claude_mpm.core.logging_utils import get_logger
|
23
24
|
from claude_mpm.core.shared.config_loader import ConfigLoader, ConfigPattern
|
24
25
|
from claude_mpm.core.unified_paths import get_path_manager
|
25
26
|
|
26
|
-
from claude_mpm.core.logging_utils import get_logger
|
27
27
|
logger = get_logger(__name__)
|
28
28
|
|
29
29
|
|
claude_mpm/config/paths.py
CHANGED
@@ -11,10 +11,11 @@ without fragile parent.parent.parent patterns.
|
|
11
11
|
from pathlib import Path
|
12
12
|
from typing import Optional, Union
|
13
13
|
|
14
|
+
from claude_mpm.core.logging_utils import get_logger
|
15
|
+
|
14
16
|
# Import from the unified path management system
|
15
17
|
from ..core.unified_paths import get_path_manager
|
16
18
|
|
17
|
-
from claude_mpm.core.logging_utils import get_logger
|
18
19
|
logger = get_logger(__name__)
|
19
20
|
|
20
21
|
|
claude_mpm/core/config.py
CHANGED
@@ -14,11 +14,12 @@ from typing import Any, Dict, List, Optional, Tuple, Union
|
|
14
14
|
|
15
15
|
import yaml
|
16
16
|
|
17
|
+
from claude_mpm.core.logging_utils import get_logger
|
18
|
+
|
17
19
|
from ..utils.config_manager import ConfigurationManager
|
18
20
|
from .exceptions import ConfigurationError, FileOperationError
|
19
21
|
from .unified_paths import get_path_manager
|
20
22
|
|
21
|
-
from claude_mpm.core.logging_utils import get_logger
|
22
23
|
logger = get_logger(__name__)
|
23
24
|
|
24
25
|
|
@@ -16,10 +16,11 @@ Aliases are stored in ~/.claude-mpm/config_aliases.json
|
|
16
16
|
import json
|
17
17
|
from typing import Dict, List, Optional, Tuple
|
18
18
|
|
19
|
+
from claude_mpm.core.logging_utils import get_logger
|
20
|
+
|
19
21
|
from ..utils.config_manager import ConfigurationManager
|
20
22
|
from .unified_paths import get_path_manager
|
21
23
|
|
22
|
-
from claude_mpm.core.logging_utils import get_logger
|
23
24
|
logger = get_logger(__name__)
|
24
25
|
|
25
26
|
|
claude_mpm/core/file_utils.py
CHANGED
@@ -22,7 +22,6 @@ from claude_mpm.core.logging_utils import get_logger
|
|
22
22
|
logger = get_logger(__name__)
|
23
23
|
|
24
24
|
|
25
|
-
|
26
25
|
# ==============================================================================
|
27
26
|
# PATH UTILITIES
|
28
27
|
# ==============================================================================
|
@@ -0,0 +1,38 @@
|
|
1
|
+
"""Framework module for Claude MPM.
|
2
|
+
|
3
|
+
This module provides the modular framework loading system with specialized components
|
4
|
+
for handling different aspects of framework initialization and management.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from .formatters import (
|
8
|
+
CapabilityGenerator,
|
9
|
+
ContentFormatter,
|
10
|
+
ContextGenerator,
|
11
|
+
)
|
12
|
+
from .loaders import (
|
13
|
+
AgentLoader,
|
14
|
+
FileLoader,
|
15
|
+
InstructionLoader,
|
16
|
+
PackagedLoader,
|
17
|
+
)
|
18
|
+
from .processors import (
|
19
|
+
MemoryProcessor,
|
20
|
+
MetadataProcessor,
|
21
|
+
TemplateProcessor,
|
22
|
+
)
|
23
|
+
|
24
|
+
__all__ = [
|
25
|
+
# Loaders
|
26
|
+
"FileLoader",
|
27
|
+
"PackagedLoader",
|
28
|
+
"InstructionLoader",
|
29
|
+
"AgentLoader",
|
30
|
+
# Formatters
|
31
|
+
"ContentFormatter",
|
32
|
+
"CapabilityGenerator",
|
33
|
+
"ContextGenerator",
|
34
|
+
# Processors
|
35
|
+
"MetadataProcessor",
|
36
|
+
"TemplateProcessor",
|
37
|
+
"MemoryProcessor",
|
38
|
+
]
|
@@ -0,0 +1,11 @@
|
|
1
|
+
"""Framework formatters for content generation and formatting."""
|
2
|
+
|
3
|
+
from .capability_generator import CapabilityGenerator
|
4
|
+
from .content_formatter import ContentFormatter
|
5
|
+
from .context_generator import ContextGenerator
|
6
|
+
|
7
|
+
__all__ = [
|
8
|
+
"CapabilityGenerator",
|
9
|
+
"ContentFormatter",
|
10
|
+
"ContextGenerator",
|
11
|
+
]
|
@@ -0,0 +1,367 @@
|
|
1
|
+
"""Agent capability generator for dynamic agent discovery."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import Any, Dict, List, Optional
|
6
|
+
|
7
|
+
import yaml
|
8
|
+
|
9
|
+
from claude_mpm.core.logging_utils import get_logger
|
10
|
+
|
11
|
+
# Import resource handling for packaged installations
|
12
|
+
try:
|
13
|
+
from importlib.resources import files
|
14
|
+
except ImportError:
|
15
|
+
try:
|
16
|
+
from importlib_resources import files
|
17
|
+
except ImportError:
|
18
|
+
files = None
|
19
|
+
|
20
|
+
|
21
|
+
class CapabilityGenerator:
|
22
|
+
"""Generates agent capability sections from deployed agents."""
|
23
|
+
|
24
|
+
def __init__(self):
|
25
|
+
"""Initialize the capability generator."""
|
26
|
+
self.logger = get_logger("capability_generator")
|
27
|
+
|
28
|
+
def generate_capabilities_section(
|
29
|
+
self,
|
30
|
+
deployed_agents: List[Dict[str, Any]],
|
31
|
+
local_agents: Dict[str, Dict[str, Any]],
|
32
|
+
) -> str:
|
33
|
+
"""Generate dynamic agent capabilities section.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
deployed_agents: List of deployed agent metadata
|
37
|
+
local_agents: Dictionary of local JSON template agents
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
Formatted capabilities section string
|
41
|
+
"""
|
42
|
+
# Build capabilities section
|
43
|
+
section = "\n\n## Available Agent Capabilities\n\n"
|
44
|
+
|
45
|
+
# Combine deployed and local agents
|
46
|
+
all_agents = {} # key: agent_id, value: (agent_data, priority)
|
47
|
+
|
48
|
+
# Add local agents first (highest priority)
|
49
|
+
for agent_id, agent_data in local_agents.items():
|
50
|
+
all_agents[agent_id] = (agent_data, -1) # Priority -1 for local agents
|
51
|
+
|
52
|
+
# Add deployed agents with lower priority
|
53
|
+
for priority, agent_data in enumerate(deployed_agents):
|
54
|
+
agent_id = agent_data["id"]
|
55
|
+
# Only add if not already present
|
56
|
+
if agent_id not in all_agents:
|
57
|
+
all_agents[agent_id] = (agent_data, priority)
|
58
|
+
|
59
|
+
# Extract just the agent data and sort
|
60
|
+
final_agents = [agent_data for agent_data, _ in all_agents.values()]
|
61
|
+
|
62
|
+
if not final_agents:
|
63
|
+
return self.get_fallback_capabilities()
|
64
|
+
|
65
|
+
# Sort agents alphabetically by ID
|
66
|
+
final_agents.sort(key=lambda x: x["id"])
|
67
|
+
|
68
|
+
# Display all agents with their rich descriptions
|
69
|
+
for agent in final_agents:
|
70
|
+
# Clean up display name - handle common acronyms
|
71
|
+
display_name = agent.get("display_name", agent["id"])
|
72
|
+
display_name = (
|
73
|
+
display_name.replace("Qa ", "QA ")
|
74
|
+
.replace("Ui ", "UI ")
|
75
|
+
.replace("Api ", "API ")
|
76
|
+
)
|
77
|
+
if display_name.lower() == "qa agent":
|
78
|
+
display_name = "QA Agent"
|
79
|
+
|
80
|
+
# Add local indicator if this is a local agent
|
81
|
+
if agent.get("is_local"):
|
82
|
+
tier_label = f" [LOCAL-{agent.get('tier', 'PROJECT').upper()}]"
|
83
|
+
section += f"\n### {display_name} (`{agent['id']}`) {tier_label}\n"
|
84
|
+
else:
|
85
|
+
section += f"\n### {display_name} (`{agent['id']}`)\n"
|
86
|
+
|
87
|
+
section += f"{agent.get('description', 'Specialized agent')}\n"
|
88
|
+
|
89
|
+
# Add routing information if available
|
90
|
+
if agent.get("routing"):
|
91
|
+
routing = agent["routing"]
|
92
|
+
routing_hints = []
|
93
|
+
|
94
|
+
if routing.get("keywords"):
|
95
|
+
# Show first 5 keywords for brevity
|
96
|
+
keywords = routing["keywords"][:5]
|
97
|
+
routing_hints.append(f"Keywords: {', '.join(keywords)}")
|
98
|
+
|
99
|
+
if routing.get("paths"):
|
100
|
+
# Show first 3 paths for brevity
|
101
|
+
paths = routing["paths"][:3]
|
102
|
+
routing_hints.append(f"Paths: {', '.join(paths)}")
|
103
|
+
|
104
|
+
if routing.get("priority"):
|
105
|
+
routing_hints.append(f"Priority: {routing['priority']}")
|
106
|
+
|
107
|
+
if routing_hints:
|
108
|
+
section += f"- **Routing**: {' | '.join(routing_hints)}\n"
|
109
|
+
|
110
|
+
# Add when_to_use if present
|
111
|
+
if routing.get("when_to_use"):
|
112
|
+
section += f"- **When to use**: {routing['when_to_use']}\n"
|
113
|
+
|
114
|
+
# Add any additional metadata if present
|
115
|
+
if agent.get("authority"):
|
116
|
+
section += f"- **Authority**: {agent['authority']}\n"
|
117
|
+
if agent.get("primary_function"):
|
118
|
+
section += f"- **Primary Function**: {agent['primary_function']}\n"
|
119
|
+
if agent.get("handoff_to"):
|
120
|
+
section += f"- **Handoff To**: {agent['handoff_to']}\n"
|
121
|
+
if agent.get("tools") and agent["tools"] != "standard":
|
122
|
+
section += f"- **Tools**: {agent['tools']}\n"
|
123
|
+
if agent.get("model") and agent["model"] != "opus":
|
124
|
+
section += f"- **Model**: {agent['model']}\n"
|
125
|
+
|
126
|
+
# Add memory routing information if available
|
127
|
+
if agent.get("memory_routing"):
|
128
|
+
memory_routing = agent["memory_routing"]
|
129
|
+
if memory_routing.get("description"):
|
130
|
+
section += (
|
131
|
+
f"- **Memory Routing**: {memory_routing['description']}\n"
|
132
|
+
)
|
133
|
+
|
134
|
+
# Add simple Context-Aware Agent Selection
|
135
|
+
section += "\n## Context-Aware Agent Selection\n\n"
|
136
|
+
section += "Select agents based on their descriptions above. Key principles:\n"
|
137
|
+
section += "- **PM questions** → Answer directly (only exception)\n"
|
138
|
+
section += "- Match task requirements to agent descriptions and authority\n"
|
139
|
+
section += "- Consider agent handoff recommendations\n"
|
140
|
+
section += "- Use the agent ID in parentheses when delegating via Task tool\n"
|
141
|
+
|
142
|
+
# Add summary
|
143
|
+
section += f"\n**Total Available Agents**: {len(final_agents)}\n"
|
144
|
+
|
145
|
+
return section
|
146
|
+
|
147
|
+
def parse_agent_metadata(self, agent_file: Path) -> Optional[Dict[str, Any]]:
|
148
|
+
"""Parse agent metadata from deployed agent file.
|
149
|
+
|
150
|
+
Args:
|
151
|
+
agent_file: Path to deployed agent file
|
152
|
+
|
153
|
+
Returns:
|
154
|
+
Dictionary with agent metadata or None
|
155
|
+
"""
|
156
|
+
try:
|
157
|
+
with open(agent_file) as f:
|
158
|
+
content = f.read()
|
159
|
+
|
160
|
+
# Default values
|
161
|
+
agent_data = {
|
162
|
+
"id": agent_file.stem,
|
163
|
+
"display_name": agent_file.stem.replace("_", " ")
|
164
|
+
.replace("-", " ")
|
165
|
+
.title(),
|
166
|
+
"description": "Specialized agent",
|
167
|
+
}
|
168
|
+
|
169
|
+
# Extract YAML frontmatter if present
|
170
|
+
if content.startswith("---"):
|
171
|
+
end_marker = content.find("---", 3)
|
172
|
+
if end_marker > 0:
|
173
|
+
frontmatter = content[3:end_marker]
|
174
|
+
metadata = yaml.safe_load(frontmatter)
|
175
|
+
if metadata:
|
176
|
+
# Use name as ID for Task tool
|
177
|
+
agent_data["id"] = metadata.get("name", agent_data["id"])
|
178
|
+
agent_data["display_name"] = (
|
179
|
+
metadata.get("name", agent_data["display_name"])
|
180
|
+
.replace("-", " ")
|
181
|
+
.title()
|
182
|
+
)
|
183
|
+
|
184
|
+
# Copy all metadata fields directly
|
185
|
+
for key, value in metadata.items():
|
186
|
+
if key not in ["name"]: # Skip already processed fields
|
187
|
+
agent_data[key] = value
|
188
|
+
|
189
|
+
# Try to load routing metadata from JSON template if not in YAML frontmatter
|
190
|
+
if "routing" not in agent_data:
|
191
|
+
routing_data = self.load_routing_from_template(agent_file.stem)
|
192
|
+
if routing_data:
|
193
|
+
agent_data["routing"] = routing_data
|
194
|
+
|
195
|
+
# Try to load memory routing metadata from JSON template
|
196
|
+
if "memory_routing" not in agent_data:
|
197
|
+
memory_routing_data = self.load_memory_routing_from_template(
|
198
|
+
agent_file.stem
|
199
|
+
)
|
200
|
+
if memory_routing_data:
|
201
|
+
agent_data["memory_routing"] = memory_routing_data
|
202
|
+
|
203
|
+
return agent_data
|
204
|
+
|
205
|
+
except Exception as e:
|
206
|
+
self.logger.debug(f"Could not parse metadata from {agent_file}: {e}")
|
207
|
+
return None
|
208
|
+
|
209
|
+
def load_routing_from_template(
|
210
|
+
self, agent_name: str, framework_path: Optional[Path] = None
|
211
|
+
) -> Optional[Dict[str, Any]]:
|
212
|
+
"""Load routing metadata from agent JSON template.
|
213
|
+
|
214
|
+
Args:
|
215
|
+
agent_name: Name of the agent
|
216
|
+
framework_path: Path to framework installation
|
217
|
+
|
218
|
+
Returns:
|
219
|
+
Dictionary with routing metadata or None if not found
|
220
|
+
"""
|
221
|
+
try:
|
222
|
+
# Check if we have a framework path
|
223
|
+
if not framework_path or framework_path == Path("__PACKAGED__"):
|
224
|
+
# For packaged installations, try to load from package resources
|
225
|
+
if files:
|
226
|
+
try:
|
227
|
+
templates_package = files("claude_mpm.agents.templates")
|
228
|
+
template_file = templates_package / f"{agent_name}.json"
|
229
|
+
|
230
|
+
if template_file.is_file():
|
231
|
+
template_content = template_file.read_text()
|
232
|
+
template_data = json.loads(template_content)
|
233
|
+
return template_data.get("routing")
|
234
|
+
except Exception as e:
|
235
|
+
self.logger.debug(
|
236
|
+
f"Could not load routing from packaged template for {agent_name}: {e}"
|
237
|
+
)
|
238
|
+
return None
|
239
|
+
|
240
|
+
# For development mode, load from filesystem
|
241
|
+
templates_dir = (
|
242
|
+
framework_path / "src" / "claude_mpm" / "agents" / "templates"
|
243
|
+
)
|
244
|
+
template_file = templates_dir / f"{agent_name}.json"
|
245
|
+
|
246
|
+
if template_file.exists():
|
247
|
+
with open(template_file) as f:
|
248
|
+
template_data = json.load(f)
|
249
|
+
return template_data.get("routing")
|
250
|
+
|
251
|
+
# Also check for variations in naming (underscore vs dash)
|
252
|
+
alternative_names = list(
|
253
|
+
{
|
254
|
+
agent_name.replace("-", "_"),
|
255
|
+
agent_name.replace("_", "-"),
|
256
|
+
agent_name.replace("-", ""),
|
257
|
+
agent_name.replace("_", ""),
|
258
|
+
}
|
259
|
+
)
|
260
|
+
|
261
|
+
for alt_name in alternative_names:
|
262
|
+
if alt_name != agent_name:
|
263
|
+
alt_file = templates_dir / f"{alt_name}.json"
|
264
|
+
if alt_file.exists():
|
265
|
+
with open(alt_file) as f:
|
266
|
+
template_data = json.load(f)
|
267
|
+
return template_data.get("routing")
|
268
|
+
|
269
|
+
return None
|
270
|
+
|
271
|
+
except Exception as e:
|
272
|
+
self.logger.debug(f"Could not load routing metadata for {agent_name}: {e}")
|
273
|
+
return None
|
274
|
+
|
275
|
+
def load_memory_routing_from_template(
|
276
|
+
self, agent_name: str, framework_path: Optional[Path] = None
|
277
|
+
) -> Optional[Dict[str, Any]]:
|
278
|
+
"""Load memory routing metadata from agent JSON template.
|
279
|
+
|
280
|
+
Args:
|
281
|
+
agent_name: Name of the agent
|
282
|
+
framework_path: Path to framework installation
|
283
|
+
|
284
|
+
Returns:
|
285
|
+
Dictionary with memory routing metadata or None if not found
|
286
|
+
"""
|
287
|
+
try:
|
288
|
+
# Check if we have a framework path
|
289
|
+
if not framework_path or framework_path == Path("__PACKAGED__"):
|
290
|
+
# For packaged installations, try to load from package resources
|
291
|
+
if files:
|
292
|
+
try:
|
293
|
+
templates_package = files("claude_mpm.agents.templates")
|
294
|
+
template_file = templates_package / f"{agent_name}.json"
|
295
|
+
|
296
|
+
if template_file.is_file():
|
297
|
+
template_content = template_file.read_text()
|
298
|
+
template_data = json.loads(template_content)
|
299
|
+
return template_data.get("memory_routing")
|
300
|
+
except Exception as e:
|
301
|
+
self.logger.debug(
|
302
|
+
f"Could not load memory routing from packaged template for {agent_name}: {e}"
|
303
|
+
)
|
304
|
+
return None
|
305
|
+
|
306
|
+
# For development mode, load from filesystem
|
307
|
+
templates_dir = (
|
308
|
+
framework_path / "src" / "claude_mpm" / "agents" / "templates"
|
309
|
+
)
|
310
|
+
template_file = templates_dir / f"{agent_name}.json"
|
311
|
+
|
312
|
+
if template_file.exists():
|
313
|
+
with open(template_file) as f:
|
314
|
+
template_data = json.load(f)
|
315
|
+
return template_data.get("memory_routing")
|
316
|
+
|
317
|
+
# Also check for variations in naming
|
318
|
+
alternative_names = list(
|
319
|
+
{
|
320
|
+
agent_name.replace("-", "_"),
|
321
|
+
agent_name.replace("_", "-"),
|
322
|
+
agent_name.replace("-agent", ""),
|
323
|
+
agent_name.replace("_agent", ""),
|
324
|
+
agent_name + "_agent",
|
325
|
+
agent_name + "-agent",
|
326
|
+
}
|
327
|
+
)
|
328
|
+
|
329
|
+
for alt_name in alternative_names:
|
330
|
+
if alt_name != agent_name:
|
331
|
+
alt_file = templates_dir / f"{alt_name}.json"
|
332
|
+
if alt_file.exists():
|
333
|
+
with open(alt_file) as f:
|
334
|
+
template_data = json.load(f)
|
335
|
+
return template_data.get("memory_routing")
|
336
|
+
|
337
|
+
return None
|
338
|
+
|
339
|
+
except Exception as e:
|
340
|
+
self.logger.debug(
|
341
|
+
f"Could not load memory routing from template for {agent_name}: {e}"
|
342
|
+
)
|
343
|
+
return None
|
344
|
+
|
345
|
+
def get_fallback_capabilities(self) -> str:
|
346
|
+
"""Return fallback capabilities when dynamic discovery fails.
|
347
|
+
|
348
|
+
Returns:
|
349
|
+
Fallback agent capabilities section
|
350
|
+
"""
|
351
|
+
return """
|
352
|
+
|
353
|
+
## Available Agent Capabilities
|
354
|
+
|
355
|
+
You have the following specialized agents available for delegation:
|
356
|
+
|
357
|
+
- **Engineer** (`engineer`): Code implementation and development
|
358
|
+
- **Research** (`research-agent`): Investigation and analysis
|
359
|
+
- **QA** (`qa-agent`): Testing and quality assurance
|
360
|
+
- **Documentation** (`documentation-agent`): Documentation creation and maintenance
|
361
|
+
- **Security** (`security-agent`): Security analysis and protection
|
362
|
+
- **Data Engineer** (`data-engineer`): Data management and pipelines
|
363
|
+
- **Ops** (`ops-agent`): Deployment and operations
|
364
|
+
- **Version Control** (`version-control`): Git operations and version management
|
365
|
+
|
366
|
+
**IMPORTANT**: Use the exact agent ID in parentheses when delegating tasks.
|
367
|
+
"""
|