claude-mpm 5.0.9__py3-none-any.whl → 5.4.41__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +4 -0
- claude_mpm/agents/BASE_AGENT.md +164 -0
- claude_mpm/agents/{PM_INSTRUCTIONS_TEACH.md → CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md} +721 -41
- claude_mpm/agents/MEMORY.md +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +468 -468
- claude_mpm/agents/WORKFLOW.md +5 -254
- claude_mpm/agents/agent_loader.py +13 -44
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/frontmatter_validator.py +70 -2
- claude_mpm/agents/templates/circuit-breakers.md +431 -45
- claude_mpm/cli/__init__.py +0 -1
- claude_mpm/cli/__main__.py +4 -0
- claude_mpm/cli/chrome_devtools_installer.py +175 -0
- claude_mpm/cli/commands/agent_state_manager.py +18 -27
- claude_mpm/cli/commands/agents.py +175 -37
- claude_mpm/cli/commands/auto_configure.py +723 -236
- claude_mpm/cli/commands/config.py +88 -2
- claude_mpm/cli/commands/configure.py +1262 -157
- claude_mpm/cli/commands/configure_agent_display.py +25 -6
- claude_mpm/cli/commands/mpm_init/core.py +225 -46
- claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
- claude_mpm/cli/commands/postmortem.py +1 -1
- claude_mpm/cli/commands/profile.py +277 -0
- claude_mpm/cli/commands/skills.py +214 -189
- claude_mpm/cli/commands/summarize.py +413 -0
- claude_mpm/cli/executor.py +21 -3
- claude_mpm/cli/interactive/agent_wizard.py +85 -10
- claude_mpm/cli/parsers/agents_parser.py +54 -9
- claude_mpm/cli/parsers/auto_configure_parser.py +13 -138
- claude_mpm/cli/parsers/base_parser.py +12 -0
- claude_mpm/cli/parsers/config_parser.py +153 -83
- claude_mpm/cli/parsers/profile_parser.py +148 -0
- claude_mpm/cli/parsers/skills_parser.py +3 -2
- claude_mpm/cli/startup.py +879 -149
- claude_mpm/commands/mpm-config.md +28 -0
- claude_mpm/commands/mpm-doctor.md +9 -22
- claude_mpm/commands/mpm-help.md +5 -287
- claude_mpm/commands/mpm-init.md +81 -507
- claude_mpm/commands/mpm-monitor.md +15 -402
- claude_mpm/commands/mpm-organize.md +120 -0
- claude_mpm/commands/mpm-postmortem.md +6 -108
- claude_mpm/commands/mpm-session-resume.md +12 -363
- claude_mpm/commands/mpm-status.md +5 -69
- claude_mpm/commands/mpm-ticket-view.md +52 -495
- claude_mpm/commands/mpm-version.md +5 -107
- claude_mpm/config/agent_sources.py +27 -0
- claude_mpm/core/config.py +2 -4
- claude_mpm/core/framework/formatters/content_formatter.py +3 -13
- claude_mpm/core/framework/loaders/agent_loader.py +8 -5
- claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
- claude_mpm/core/framework_loader.py +4 -2
- claude_mpm/core/logger.py +13 -0
- claude_mpm/core/optimized_startup.py +59 -0
- claude_mpm/core/output_style_manager.py +173 -43
- claude_mpm/core/shared/config_loader.py +1 -1
- claude_mpm/core/socketio_pool.py +3 -3
- claude_mpm/core/unified_agent_registry.py +134 -16
- claude_mpm/core/unified_config.py +22 -0
- claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
- claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
- claude_mpm/dashboard/static/svelte-build/index.html +36 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
- claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
- claude_mpm/hooks/claude_hooks/installer.py +33 -10
- claude_mpm/hooks/claude_hooks/memory_integration.py +28 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +30 -6
- claude_mpm/hooks/memory_integration_hook.py +46 -1
- claude_mpm/init.py +63 -19
- claude_mpm/models/agent_definition.py +7 -0
- claude_mpm/models/git_repository.py +3 -3
- claude_mpm/scripts/claude-hook-handler.sh +58 -18
- claude_mpm/scripts/launch_monitor.py +93 -13
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/agent_builder.py +3 -3
- claude_mpm/services/agents/agent_recommendation_service.py +278 -0
- claude_mpm/services/agents/agent_review_service.py +280 -0
- claude_mpm/services/agents/cache_git_manager.py +6 -6
- claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
- claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -5
- claude_mpm/services/agents/deployment/agent_template_builder.py +5 -3
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +320 -29
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +546 -68
- claude_mpm/services/agents/git_source_manager.py +36 -2
- claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
- claude_mpm/services/agents/recommender.py +5 -3
- claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
- claude_mpm/services/agents/sources/git_source_sync_service.py +13 -6
- claude_mpm/services/agents/startup_sync.py +22 -2
- claude_mpm/services/agents/toolchain_detector.py +10 -6
- claude_mpm/services/analysis/__init__.py +11 -1
- claude_mpm/services/analysis/clone_detector.py +1030 -0
- claude_mpm/services/command_deployment_service.py +81 -10
- claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
- claude_mpm/services/event_bus/config.py +3 -1
- claude_mpm/services/git/git_operations_service.py +101 -16
- claude_mpm/services/monitor/daemon.py +9 -2
- claude_mpm/services/monitor/daemon_manager.py +39 -3
- claude_mpm/services/monitor/management/lifecycle.py +8 -1
- claude_mpm/services/monitor/server.py +698 -22
- claude_mpm/services/pm_skills_deployer.py +676 -0
- claude_mpm/services/profile_manager.py +331 -0
- claude_mpm/services/project/project_organizer.py +4 -0
- claude_mpm/services/self_upgrade_service.py +120 -12
- claude_mpm/services/skills/__init__.py +3 -0
- claude_mpm/services/skills/git_skill_source_manager.py +130 -2
- claude_mpm/services/skills/selective_skill_deployer.py +704 -0
- claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
- claude_mpm/services/skills_deployer.py +126 -9
- claude_mpm/services/socketio/dashboard_server.py +1 -0
- claude_mpm/services/socketio/event_normalizer.py +51 -6
- claude_mpm/services/socketio/server/core.py +386 -108
- claude_mpm/services/version_control/git_operations.py +103 -0
- claude_mpm/skills/skill_manager.py +92 -3
- claude_mpm/utils/agent_dependency_loader.py +14 -2
- claude_mpm/utils/agent_filters.py +17 -44
- claude_mpm/utils/gitignore.py +3 -0
- claude_mpm/utils/migration.py +4 -4
- claude_mpm/utils/robust_installer.py +47 -3
- {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/METADATA +57 -87
- {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/RECORD +160 -211
- claude_mpm-5.4.41.dist-info/entry_points.txt +5 -0
- claude_mpm-5.4.41.dist-info/licenses/LICENSE +94 -0
- claude_mpm-5.4.41.dist-info/licenses/LICENSE-FAQ.md +153 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
- claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
- claude_mpm/agents/BASE_OPS.md +0 -219
- claude_mpm/agents/BASE_PM.md +0 -480
- claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
- claude_mpm/agents/BASE_QA.md +0 -167
- claude_mpm/agents/BASE_RESEARCH.md +0 -53
- claude_mpm/agents/base_agent_loader.py +0 -601
- claude_mpm/cli/commands/agents_detect.py +0 -380
- claude_mpm/cli/commands/agents_recommend.py +0 -309
- claude_mpm/cli/ticket_cli.py +0 -35
- claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
- claude_mpm/commands/mpm-agents-detect.md +0 -177
- claude_mpm/commands/mpm-agents-list.md +0 -131
- claude_mpm/commands/mpm-agents-recommend.md +0 -223
- claude_mpm/commands/mpm-config-view.md +0 -150
- claude_mpm/commands/mpm-ticket-organize.md +0 -304
- claude_mpm/dashboard/analysis_runner.py +0 -455
- claude_mpm/dashboard/index.html +0 -13
- claude_mpm/dashboard/open_dashboard.py +0 -66
- claude_mpm/dashboard/static/css/activity.css +0 -1958
- claude_mpm/dashboard/static/css/connection-status.css +0 -370
- claude_mpm/dashboard/static/css/dashboard.css +0 -4701
- claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
- claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
- claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
- claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
- claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
- claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
- claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
- claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
- claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
- claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
- claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
- claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
- claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
- claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
- claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
- claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
- claude_mpm/dashboard/static/js/connection-manager.js +0 -536
- claude_mpm/dashboard/static/js/dashboard.js +0 -1914
- claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
- claude_mpm/dashboard/static/js/socket-client.js +0 -1474
- claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
- claude_mpm/dashboard/static/socket.io.min.js +0 -7
- claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
- claude_mpm/dashboard/templates/code_simple.html +0 -153
- claude_mpm/dashboard/templates/index.html +0 -606
- claude_mpm/dashboard/test_dashboard.html +0 -372
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
- claude_mpm/scripts/mcp_server.py +0 -75
- claude_mpm/scripts/mcp_wrapper.py +0 -39
- claude_mpm/services/mcp_gateway/__init__.py +0 -159
- claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
- claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
- claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
- claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
- claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
- claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
- claude_mpm/services/mcp_gateway/core/base.py +0 -312
- claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
- claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
- claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
- claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
- claude_mpm/services/mcp_gateway/main.py +0 -589
- claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
- claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
- claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
- claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
- claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
- claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
- claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
- claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
- claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
- claude_mpm-5.0.9.dist-info/entry_points.txt +0 -10
- claude_mpm-5.0.9.dist-info/licenses/LICENSE +0 -21
- /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
- {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/WHEEL +0 -0
- {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"""Agent review service for comparing project agents with managed agents.
|
|
2
|
+
|
|
3
|
+
WHY: This service helps users maintain a clean agent directory by:
|
|
4
|
+
1. Identifying which agents are managed vs custom
|
|
5
|
+
2. Detecting outdated versions of managed agents
|
|
6
|
+
3. Finding unused agents that don't match the detected toolchain
|
|
7
|
+
4. Safely archiving unnecessary agents instead of deleting them
|
|
8
|
+
|
|
9
|
+
DESIGN DECISIONS:
|
|
10
|
+
- Archive to .claude/agents/unused/ instead of deleting (safe, recoverable)
|
|
11
|
+
- Add timestamps to archived files to prevent conflicts
|
|
12
|
+
- Preserve custom user agents (not in managed set)
|
|
13
|
+
- Compare versions to detect outdated managed agents
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import shutil
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any, Dict, List, Set
|
|
20
|
+
|
|
21
|
+
from claude_mpm.core.logging_config import get_logger
|
|
22
|
+
|
|
23
|
+
logger = get_logger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AgentReviewService:
|
|
27
|
+
"""Service for reviewing and managing project agents.
|
|
28
|
+
|
|
29
|
+
This service analyzes the relationship between project agents and managed
|
|
30
|
+
agents from the claude-mpm-agents repository, categorizing them as:
|
|
31
|
+
- Managed: In sync with managed agents
|
|
32
|
+
- Outdated: Older version of managed agent exists
|
|
33
|
+
- Custom: User-created agents not in managed set
|
|
34
|
+
- Unused: Not recommended for this project's toolchain
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self):
|
|
38
|
+
"""Initialize the agent review service."""
|
|
39
|
+
self.logger = get_logger(__name__)
|
|
40
|
+
|
|
41
|
+
def review_project_agents(
|
|
42
|
+
self,
|
|
43
|
+
project_agents_dir: Path,
|
|
44
|
+
managed_agents: List[Dict[str, Any]],
|
|
45
|
+
recommended_agent_ids: Set[str],
|
|
46
|
+
) -> Dict[str, List[Dict[str, Any]]]:
|
|
47
|
+
"""Review existing project agents and categorize them.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
project_agents_dir: Directory containing project agents (.claude/agents/)
|
|
51
|
+
managed_agents: List of managed agent dicts from cache
|
|
52
|
+
recommended_agent_ids: Set of agent IDs recommended for this toolchain
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Dictionary with categorized agents:
|
|
56
|
+
{
|
|
57
|
+
"managed": [...], # In sync with managed
|
|
58
|
+
"outdated": [...], # Older version exists
|
|
59
|
+
"custom": [...], # User-created
|
|
60
|
+
"unused": [...], # Not needed for this toolchain
|
|
61
|
+
}
|
|
62
|
+
"""
|
|
63
|
+
results = {
|
|
64
|
+
"managed": [],
|
|
65
|
+
"outdated": [],
|
|
66
|
+
"custom": [],
|
|
67
|
+
"unused": [],
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if not project_agents_dir.exists():
|
|
71
|
+
self.logger.debug(
|
|
72
|
+
f"Project agents directory does not exist: {project_agents_dir}"
|
|
73
|
+
)
|
|
74
|
+
return results
|
|
75
|
+
|
|
76
|
+
# Build lookup map of managed agents by ID
|
|
77
|
+
managed_by_id = {agent["agent_id"]: agent for agent in managed_agents}
|
|
78
|
+
|
|
79
|
+
# Scan project agents
|
|
80
|
+
for agent_file in project_agents_dir.glob("*.md"):
|
|
81
|
+
# Skip the unused directory itself
|
|
82
|
+
if agent_file.name == "unused":
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
agent_name = agent_file.stem
|
|
86
|
+
|
|
87
|
+
# Parse agent to get version and metadata
|
|
88
|
+
project_agent_info = self._parse_project_agent(agent_file)
|
|
89
|
+
|
|
90
|
+
# Check if this is a managed agent
|
|
91
|
+
if agent_name in managed_by_id:
|
|
92
|
+
managed_agent = managed_by_id[agent_name]
|
|
93
|
+
|
|
94
|
+
# Compare versions
|
|
95
|
+
project_version = project_agent_info.get("version", "unknown")
|
|
96
|
+
managed_version = managed_agent.get("version", "unknown")
|
|
97
|
+
|
|
98
|
+
if self._is_outdated(project_version, managed_version):
|
|
99
|
+
# Outdated version of managed agent
|
|
100
|
+
results["outdated"].append(
|
|
101
|
+
{
|
|
102
|
+
"name": agent_name,
|
|
103
|
+
"path": agent_file,
|
|
104
|
+
"current_version": project_version,
|
|
105
|
+
"available_version": managed_version,
|
|
106
|
+
"recommended": agent_name in recommended_agent_ids,
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
else:
|
|
110
|
+
# Up-to-date managed agent
|
|
111
|
+
results["managed"].append(
|
|
112
|
+
{
|
|
113
|
+
"name": agent_name,
|
|
114
|
+
"path": agent_file,
|
|
115
|
+
"version": project_version,
|
|
116
|
+
"recommended": agent_name in recommended_agent_ids,
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
# Custom user agent (not in managed set)
|
|
121
|
+
results["custom"].append(
|
|
122
|
+
{
|
|
123
|
+
"name": agent_name,
|
|
124
|
+
"path": agent_file,
|
|
125
|
+
"version": project_agent_info.get("version", "unknown"),
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Identify unused agents (managed or outdated but not recommended)
|
|
130
|
+
for category in ["managed", "outdated"]:
|
|
131
|
+
for agent in results[category][:]: # Copy list to modify during iteration
|
|
132
|
+
if not agent.get("recommended", False):
|
|
133
|
+
# This managed/outdated agent is not recommended for this toolchain
|
|
134
|
+
results["unused"].append(agent)
|
|
135
|
+
results[category].remove(agent)
|
|
136
|
+
|
|
137
|
+
self.logger.info(
|
|
138
|
+
f"Agent review complete: "
|
|
139
|
+
f"{len(results['managed'])} managed, "
|
|
140
|
+
f"{len(results['outdated'])} outdated, "
|
|
141
|
+
f"{len(results['custom'])} custom, "
|
|
142
|
+
f"{len(results['unused'])} unused"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return results
|
|
146
|
+
|
|
147
|
+
def archive_agents(
|
|
148
|
+
self, agents_to_archive: List[Dict[str, Any]], project_agents_dir: Path
|
|
149
|
+
) -> Dict[str, Any]:
|
|
150
|
+
"""Archive agents by moving them to .claude/agents/unused/.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
agents_to_archive: List of agent dicts with 'name' and 'path' keys
|
|
154
|
+
project_agents_dir: Base agents directory (.claude/agents/)
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Dictionary with archival results:
|
|
158
|
+
{
|
|
159
|
+
"archived": [...], # Successfully archived
|
|
160
|
+
"errors": [...], # Archival errors
|
|
161
|
+
}
|
|
162
|
+
"""
|
|
163
|
+
results = {"archived": [], "errors": []}
|
|
164
|
+
|
|
165
|
+
if not agents_to_archive:
|
|
166
|
+
return results
|
|
167
|
+
|
|
168
|
+
# Create unused directory
|
|
169
|
+
unused_dir = project_agents_dir / "unused"
|
|
170
|
+
unused_dir.mkdir(exist_ok=True)
|
|
171
|
+
|
|
172
|
+
for agent in agents_to_archive:
|
|
173
|
+
agent_path = agent["path"]
|
|
174
|
+
agent_name = agent["name"]
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
# Generate timestamped filename to avoid conflicts
|
|
178
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
|
179
|
+
archived_name = f"{agent_name}_{timestamp}.md"
|
|
180
|
+
archived_path = unused_dir / archived_name
|
|
181
|
+
|
|
182
|
+
# Move the file
|
|
183
|
+
shutil.move(str(agent_path), str(archived_path))
|
|
184
|
+
|
|
185
|
+
results["archived"].append(
|
|
186
|
+
{
|
|
187
|
+
"name": agent_name,
|
|
188
|
+
"original_path": str(agent_path),
|
|
189
|
+
"archived_path": str(archived_path),
|
|
190
|
+
}
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
self.logger.debug(f"Archived {agent_name} to {archived_path}")
|
|
194
|
+
|
|
195
|
+
except Exception as e:
|
|
196
|
+
error_msg = f"Failed to archive {agent_name}: {e}"
|
|
197
|
+
self.logger.error(error_msg)
|
|
198
|
+
results["errors"].append(error_msg)
|
|
199
|
+
|
|
200
|
+
self.logger.info(
|
|
201
|
+
f"Archived {len(results['archived'])} agents, "
|
|
202
|
+
f"{len(results['errors'])} errors"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
return results
|
|
206
|
+
|
|
207
|
+
def _parse_project_agent(self, agent_file: Path) -> Dict[str, Any]:
|
|
208
|
+
"""Parse a project agent file to extract metadata.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
agent_file: Path to agent Markdown file
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Dictionary with agent metadata (version, name, etc.)
|
|
215
|
+
"""
|
|
216
|
+
try:
|
|
217
|
+
content = agent_file.read_text(encoding="utf-8")
|
|
218
|
+
|
|
219
|
+
# Extract version from YAML frontmatter
|
|
220
|
+
import re
|
|
221
|
+
|
|
222
|
+
version_match = re.search(
|
|
223
|
+
r'^version:\s*["\']?(.+?)["\']?$', content, re.MULTILINE
|
|
224
|
+
)
|
|
225
|
+
version = version_match.group(1) if version_match else "unknown"
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
"version": version,
|
|
229
|
+
"name": agent_file.stem,
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
except Exception as e:
|
|
233
|
+
self.logger.warning(f"Failed to parse agent {agent_file.name}: {e}")
|
|
234
|
+
return {"version": "unknown", "name": agent_file.stem}
|
|
235
|
+
|
|
236
|
+
def _is_outdated(self, current_version: str, available_version: str) -> bool:
|
|
237
|
+
"""Check if current version is outdated compared to available version.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
current_version: Currently deployed version
|
|
241
|
+
available_version: Available version from managed agents
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
True if current version is outdated
|
|
245
|
+
"""
|
|
246
|
+
# Handle unknown versions
|
|
247
|
+
if current_version == "unknown" or available_version == "unknown":
|
|
248
|
+
return False
|
|
249
|
+
|
|
250
|
+
# Simple string comparison for now
|
|
251
|
+
# TODO: Implement semantic version comparison (1.2.3 vs 1.2.4)
|
|
252
|
+
return current_version != available_version
|
|
253
|
+
|
|
254
|
+
def get_archive_summary(self, project_agents_dir: Path) -> Dict[str, Any]:
|
|
255
|
+
"""Get summary of archived agents.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
project_agents_dir: Base agents directory (.claude/agents/)
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Dictionary with archive statistics
|
|
262
|
+
"""
|
|
263
|
+
unused_dir = project_agents_dir / "unused"
|
|
264
|
+
|
|
265
|
+
if not unused_dir.exists():
|
|
266
|
+
return {"count": 0, "agents": []}
|
|
267
|
+
|
|
268
|
+
archived_files = list(unused_dir.glob("*.md"))
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
"count": len(archived_files),
|
|
272
|
+
"agents": [
|
|
273
|
+
{
|
|
274
|
+
"name": f.stem,
|
|
275
|
+
"path": str(f),
|
|
276
|
+
"size_bytes": f.stat().st_size,
|
|
277
|
+
}
|
|
278
|
+
for f in archived_files
|
|
279
|
+
],
|
|
280
|
+
}
|
|
@@ -29,7 +29,7 @@ Error Handling:
|
|
|
29
29
|
|
|
30
30
|
Example:
|
|
31
31
|
>>> from pathlib import Path
|
|
32
|
-
>>> manager = CacheGitManager(Path.home() / ".claude-mpm/cache/
|
|
32
|
+
>>> manager = CacheGitManager(Path.home() / ".claude-mpm/cache/agents")
|
|
33
33
|
>>> if manager.is_git_repo():
|
|
34
34
|
... status = manager.get_status()
|
|
35
35
|
... print(f"Branch: {status['branch']}, Uncommitted: {len(status['uncommitted'])}")
|
|
@@ -76,7 +76,7 @@ class CacheGitManager:
|
|
|
76
76
|
timeout: Git command timeout in seconds (default: 30)
|
|
77
77
|
|
|
78
78
|
Example:
|
|
79
|
-
>>> cache_dir = Path.home() / ".claude-mpm/cache/
|
|
79
|
+
>>> cache_dir = Path.home() / ".claude-mpm/cache/agents"
|
|
80
80
|
>>> manager = CacheGitManager(cache_dir)
|
|
81
81
|
"""
|
|
82
82
|
self.cache_path = Path(cache_path)
|
|
@@ -105,12 +105,12 @@ class CacheGitManager:
|
|
|
105
105
|
|
|
106
106
|
Example:
|
|
107
107
|
>>> # Case 1: cache_path inside repo (searches upward)
|
|
108
|
-
>>> # cache_path: ~/.claude-mpm/cache/
|
|
109
|
-
>>> # Found at: ~/.claude-mpm/cache/
|
|
108
|
+
>>> # cache_path: ~/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents/agents
|
|
109
|
+
>>> # Found at: ~/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents
|
|
110
110
|
|
|
111
111
|
>>> # Case 2: repo nested in cache_path (searches downward)
|
|
112
|
-
>>> # cache_path: ~/.claude-mpm/cache/
|
|
113
|
-
>>> # Found at: ~/.claude-mpm/cache/
|
|
112
|
+
>>> # cache_path: ~/.claude-mpm/cache/agents
|
|
113
|
+
>>> # Found at: ~/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents
|
|
114
114
|
"""
|
|
115
115
|
# Strategy 1: Search upward (cache_path is inside repo)
|
|
116
116
|
current = self.cache_path
|
|
@@ -876,13 +876,13 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
876
876
|
user_agents_dir = potential_user_dir
|
|
877
877
|
self.logger.info(f"Found user agents at: {user_agents_dir}")
|
|
878
878
|
|
|
879
|
-
# Check for
|
|
880
|
-
|
|
879
|
+
# Check for agents cache (from Git sources)
|
|
880
|
+
agents_cache_dir = None
|
|
881
881
|
cache_dir = user_home / ".claude-mpm" / "cache"
|
|
882
|
-
|
|
883
|
-
if
|
|
884
|
-
|
|
885
|
-
self.logger.info(f"Found
|
|
882
|
+
potential_cache_dir = cache_dir / "agents"
|
|
883
|
+
if potential_cache_dir.exists():
|
|
884
|
+
agents_cache_dir = potential_cache_dir
|
|
885
|
+
self.logger.info(f"Found agents cache at: {agents_cache_dir}")
|
|
886
886
|
|
|
887
887
|
# Get agents with version comparison and cleanup (4-tier discovery)
|
|
888
888
|
agents_to_deploy, agent_sources, cleanup_results = (
|
|
@@ -890,7 +890,7 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
890
890
|
system_templates_dir=system_templates_dir,
|
|
891
891
|
project_agents_dir=project_agents_dir,
|
|
892
892
|
user_agents_dir=user_agents_dir,
|
|
893
|
-
|
|
893
|
+
agents_cache_dir=agents_cache_dir, # NEW: 4th tier
|
|
894
894
|
working_directory=self.working_directory,
|
|
895
895
|
excluded_agents=excluded_agents,
|
|
896
896
|
config=config,
|
|
@@ -898,6 +898,9 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
898
898
|
)
|
|
899
899
|
)
|
|
900
900
|
|
|
901
|
+
# Keep track of all enabled agents before filtering (for cleanup)
|
|
902
|
+
all_enabled_agents = agents_to_deploy.copy()
|
|
903
|
+
|
|
901
904
|
# Compare with deployed versions if agents directory exists
|
|
902
905
|
if agents_dir.exists():
|
|
903
906
|
comparison_results = self.multi_source_service.compare_deployed_versions(
|
|
@@ -954,6 +957,25 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
954
957
|
f"All {len(comparison_results.get('up_to_date', []))} agents are up to date"
|
|
955
958
|
)
|
|
956
959
|
|
|
960
|
+
# Cleanup excluded agents (remove agents not in deployment list)
|
|
961
|
+
# CRITICAL: Use all_enabled_agents (before filtering for updates) to preserve up-to-date agents
|
|
962
|
+
# Bug fix (1M-XXX): Previously used filtered agents_to_deploy which could be empty,
|
|
963
|
+
# causing all agents to be removed when everything was up-to-date
|
|
964
|
+
exclusion_cleanup_results = self.multi_source_service.cleanup_excluded_agents(
|
|
965
|
+
deployed_agents_dir=agents_dir,
|
|
966
|
+
agents_to_deploy=all_enabled_agents,
|
|
967
|
+
)
|
|
968
|
+
|
|
969
|
+
# Add exclusion cleanup results to main cleanup results
|
|
970
|
+
if exclusion_cleanup_results.get("removed"):
|
|
971
|
+
cleanup_results.setdefault("excluded_removed", []).extend(
|
|
972
|
+
exclusion_cleanup_results["removed"]
|
|
973
|
+
)
|
|
974
|
+
self.logger.info(
|
|
975
|
+
f"Removed {len(exclusion_cleanup_results['removed'])} excluded agents: "
|
|
976
|
+
f"{', '.join(exclusion_cleanup_results['removed'])}"
|
|
977
|
+
)
|
|
978
|
+
|
|
957
979
|
# Convert to list of Path objects
|
|
958
980
|
template_files = list(agents_to_deploy.values())
|
|
959
981
|
|
|
@@ -215,9 +215,8 @@ class AgentDiscoveryService:
|
|
|
215
215
|
# Extract YAML frontmatter
|
|
216
216
|
frontmatter = self._extract_yaml_frontmatter(template_content)
|
|
217
217
|
if not frontmatter:
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
)
|
|
218
|
+
# Silently return None for files without frontmatter
|
|
219
|
+
# (e.g., PM instruction templates in templates/ directory)
|
|
221
220
|
return None
|
|
222
221
|
|
|
223
222
|
# Extract metadata directly from frontmatter (flat structure)
|
|
@@ -249,7 +248,7 @@ class AgentDiscoveryService:
|
|
|
249
248
|
return agent_info
|
|
250
249
|
|
|
251
250
|
except yaml.YAMLError as e:
|
|
252
|
-
self.logger.
|
|
251
|
+
self.logger.warning(f"Invalid YAML frontmatter in {template_file.name}: {e}")
|
|
253
252
|
return None
|
|
254
253
|
except Exception as e:
|
|
255
254
|
self.logger.error(
|
|
@@ -432,7 +431,7 @@ class AgentDiscoveryService:
|
|
|
432
431
|
return True
|
|
433
432
|
|
|
434
433
|
except yaml.YAMLError:
|
|
435
|
-
self.logger.
|
|
434
|
+
self.logger.warning(
|
|
436
435
|
f"Invalid YAML frontmatter in template: {template_file.name}"
|
|
437
436
|
)
|
|
438
437
|
return False
|
|
@@ -129,13 +129,14 @@ class AgentTemplateBuilder:
|
|
|
129
129
|
base_templates.append(base_agent_file)
|
|
130
130
|
self.logger.debug(f"Found BASE-AGENT.md at: {base_agent_file}")
|
|
131
131
|
|
|
132
|
-
# Stop at git repository root if detected
|
|
132
|
+
# Stop at git repository root if detected (check AFTER finding BASE-AGENT.md)
|
|
133
133
|
if (current_dir / ".git").exists():
|
|
134
134
|
self.logger.debug(f"Reached git repository root at: {current_dir}")
|
|
135
135
|
break
|
|
136
136
|
|
|
137
|
-
# Stop at common repository root indicators
|
|
138
|
-
|
|
137
|
+
# Stop at common repository root indicators (check AFTER finding BASE-AGENT.md)
|
|
138
|
+
# Stop at cache root or .claude-mpm directory
|
|
139
|
+
if current_dir.name in [".claude-mpm", "cache"]:
|
|
139
140
|
self.logger.debug(
|
|
140
141
|
f"Reached repository root indicator at: {current_dir}"
|
|
141
142
|
)
|
|
@@ -675,6 +676,7 @@ Only include memories that are:
|
|
|
675
676
|
"""
|
|
676
677
|
content = content + memory_instructions
|
|
677
678
|
|
|
679
|
+
# Combine frontmatter and content
|
|
678
680
|
return frontmatter + content
|
|
679
681
|
|
|
680
682
|
def build_agent_yaml(
|
|
@@ -8,7 +8,7 @@ DEPLOYMENT ARCHITECTURE:
|
|
|
8
8
|
|
|
9
9
|
Agent Source Locations (Discovery):
|
|
10
10
|
-----------------------------------
|
|
11
|
-
1. System Agents: ~/.claude-mpm/cache/
|
|
11
|
+
1. System Agents: ~/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents/
|
|
12
12
|
- Synced from GitHub repository
|
|
13
13
|
- Read-only (managed by git pull)
|
|
14
14
|
- 44+ agents organized by category
|
|
@@ -39,7 +39,7 @@ Why Project-Level Deployment?
|
|
|
39
39
|
Example Flow:
|
|
40
40
|
-------------
|
|
41
41
|
1. User runs: claude-mpm agents deploy
|
|
42
|
-
2. Agents synced from GitHub → ~/.claude-mpm/cache/
|
|
42
|
+
2. Agents synced from GitHub → ~/.claude-mpm/cache/agents/
|
|
43
43
|
3. Agents deployed FROM cache → .claude/agents/
|
|
44
44
|
4. Claude Code discovers agents FROM .claude/agents/
|
|
45
45
|
|