claude-mpm 5.4.21__py3-none-any.whl → 5.4.36__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (84) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_AGENT.md +164 -0
  3. claude_mpm/agents/BASE_ENGINEER.md +658 -0
  4. claude_mpm/agents/MEMORY.md +1 -1
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +320 -880
  6. claude_mpm/agents/WORKFLOW.md +5 -254
  7. claude_mpm/agents/agent_loader.py +1 -1
  8. claude_mpm/agents/base_agent.json +31 -0
  9. claude_mpm/cli/commands/agent_state_manager.py +10 -10
  10. claude_mpm/cli/commands/agents.py +9 -9
  11. claude_mpm/cli/commands/auto_configure.py +4 -4
  12. claude_mpm/cli/commands/configure.py +1 -1
  13. claude_mpm/cli/commands/postmortem.py +1 -1
  14. claude_mpm/cli/interactive/agent_wizard.py +2 -2
  15. claude_mpm/cli/startup.py +98 -58
  16. claude_mpm/core/config.py +2 -4
  17. claude_mpm/core/framework/loaders/agent_loader.py +1 -1
  18. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  19. claude_mpm/core/unified_agent_registry.py +1 -1
  20. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  21. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  22. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  23. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  24. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  25. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  26. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  27. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  28. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  29. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  30. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  31. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  32. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  33. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  34. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  35. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  36. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  37. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  38. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  39. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  40. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  41. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  42. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  43. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  44. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  45. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  46. claude_mpm/hooks/claude_hooks/hook_handler.py +149 -1
  47. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  48. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  49. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  50. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  51. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  52. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  53. claude_mpm/hooks/claude_hooks/services/connection_manager.py +26 -6
  54. claude_mpm/models/git_repository.py +3 -3
  55. claude_mpm/services/agents/cache_git_manager.py +6 -6
  56. claude_mpm/services/agents/deployment/agent_deployment.py +7 -7
  57. claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -2
  58. claude_mpm/services/agents/deployment/agent_template_builder.py +2 -2
  59. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  60. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +20 -22
  61. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +55 -53
  62. claude_mpm/services/agents/git_source_manager.py +2 -2
  63. claude_mpm/services/agents/recommender.py +5 -3
  64. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  65. claude_mpm/services/agents/sources/git_source_sync_service.py +5 -5
  66. claude_mpm/services/agents/startup_sync.py +22 -2
  67. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  68. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  69. claude_mpm/services/git/git_operations_service.py +8 -8
  70. claude_mpm/services/monitor/server.py +473 -3
  71. claude_mpm/services/socketio/dashboard_server.py +1 -0
  72. claude_mpm/services/socketio/event_normalizer.py +37 -6
  73. claude_mpm/services/socketio/server/core.py +262 -123
  74. claude_mpm/utils/agent_dependency_loader.py +14 -2
  75. claude_mpm/utils/agent_filters.py +1 -1
  76. claude_mpm/utils/migration.py +4 -4
  77. claude_mpm/utils/robust_installer.py +47 -3
  78. {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/METADATA +5 -3
  79. {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/RECORD +84 -49
  80. {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/WHEEL +0 -0
  81. {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/entry_points.txt +0 -0
  82. {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/licenses/LICENSE +0 -0
  83. {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  84. {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/top_level.txt +0 -0
@@ -42,7 +42,7 @@ class RemoteAgentMetadata:
42
42
  class RemoteAgentDiscoveryService:
43
43
  """Discovers and converts remote Markdown agents to JSON format.
44
44
 
45
- Remote agents are discovered from the cache directory (~/.claude-mpm/cache/remote-agents/)
45
+ Remote agents are discovered from the cache directory (~/.claude-mpm/cache/agents/)
46
46
  where they are stored as Markdown files. This service:
47
47
  1. Discovers all *.md files in the remote agents cache
48
48
  2. Parses Markdown frontmatter and content to extract metadata
@@ -61,20 +61,20 @@ class RemoteAgentDiscoveryService:
61
61
  - Flexibility: Supports optional sections with defaults
62
62
  """
63
63
 
64
- def __init__(self, remote_agents_dir: Path):
64
+ def __init__(self, agents_cache_dir: Path):
65
65
  """Initialize the remote agent discovery service.
66
66
 
67
67
  Args:
68
- remote_agents_dir: Directory containing cached remote agent Markdown files
68
+ agents_cache_dir: Directory containing cached agent Markdown files
69
69
  """
70
- self.remote_agents_dir = remote_agents_dir
70
+ self.agents_cache_dir = agents_cache_dir
71
71
  self.logger = get_logger(__name__)
72
72
 
73
73
  def _extract_collection_id_from_path(self, file_path: Path) -> Optional[str]:
74
74
  """Extract collection_id from repository path structure.
75
75
 
76
76
  Collection ID is derived from the repository path structure:
77
- ~/.claude-mpm/cache/remote-agents/{owner}/{repo}/agents/...
77
+ ~/.claude-mpm/cache/agents/{owner}/{repo}/agents/...
78
78
 
79
79
  Args:
80
80
  file_path: Absolute path to agent Markdown file
@@ -83,28 +83,29 @@ class RemoteAgentDiscoveryService:
83
83
  Collection ID in format "owner/repo-name" or None if not found
84
84
 
85
85
  Example:
86
- Input: ~/.claude-mpm/cache/remote-agents/bobmatnyc/claude-mpm-agents/agents/pm.md
86
+ Input: ~/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents/agents/pm.md
87
87
  Output: "bobmatnyc/claude-mpm-agents"
88
88
  """
89
89
  try:
90
- # Find "remote-agents" in the path
90
+ # Find "agents" cache directory in the path (looking for .claude-mpm/cache/agents)
91
91
  path_parts = file_path.parts
92
- remote_agents_idx = -1
92
+ agents_cache_idx = -1
93
93
 
94
94
  for i, part in enumerate(path_parts):
95
- if part == "remote-agents":
96
- remote_agents_idx = i
95
+ # Look for cache/agents pattern
96
+ if part == "agents" and i > 0 and path_parts[i - 1] == "cache":
97
+ agents_cache_idx = i
97
98
  break
98
99
 
99
- if remote_agents_idx == -1 or remote_agents_idx + 2 >= len(path_parts):
100
+ if agents_cache_idx == -1 or agents_cache_idx + 2 >= len(path_parts):
100
101
  self.logger.debug(
101
102
  f"Could not extract collection_id from path: {file_path}"
102
103
  )
103
104
  return None
104
105
 
105
- # Extract owner and repo (next two parts after "remote-agents")
106
- owner = path_parts[remote_agents_idx + 1]
107
- repo = path_parts[remote_agents_idx + 2]
106
+ # Extract owner and repo (next two parts after "cache/agents")
107
+ owner = path_parts[agents_cache_idx + 1]
108
+ repo = path_parts[agents_cache_idx + 2]
108
109
 
109
110
  collection_id = f"{owner}/{repo}"
110
111
  self.logger.debug(f"Extracted collection_id: {collection_id}")
@@ -128,25 +129,26 @@ class RemoteAgentDiscoveryService:
128
129
  Relative path from repo root, or None if not found
129
130
 
130
131
  Example:
131
- Input: ~/.claude-mpm/cache/remote-agents/bobmatnyc/claude-mpm-agents/agents/pm.md
132
+ Input: ~/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents/agents/pm.md
132
133
  Output: "agents/pm.md"
133
134
  """
134
135
  try:
135
- # Find "remote-agents" in the path
136
+ # Find "agents" cache directory in the path
136
137
  path_parts = file_path.parts
137
- remote_agents_idx = -1
138
+ agents_cache_idx = -1
138
139
 
139
140
  for i, part in enumerate(path_parts):
140
- if part == "remote-agents":
141
- remote_agents_idx = i
141
+ # Look for cache/agents pattern
142
+ if part == "agents" and i > 0 and path_parts[i - 1] == "cache":
143
+ agents_cache_idx = i
142
144
  break
143
145
 
144
- if remote_agents_idx == -1 or remote_agents_idx + 3 >= len(path_parts):
146
+ if agents_cache_idx == -1 or agents_cache_idx + 3 >= len(path_parts):
145
147
  return None
146
148
 
147
149
  # Path after owner/repo is the source path
148
- # remote-agents/{owner}/{repo}/{source_path}
149
- repo_root_idx = remote_agents_idx + 3
150
+ # cache/agents/{owner}/{repo}/{source_path}
151
+ repo_root_idx = agents_cache_idx + 3
150
152
  source_parts = path_parts[repo_root_idx:]
151
153
 
152
154
  return "/".join(source_parts)
@@ -273,7 +275,7 @@ class RemoteAgentDiscoveryService:
273
275
 
274
276
  Supports both cache structures:
275
277
  1. Git repo: Calculate relative to /agents/ subdirectory
276
- 2. Flattened cache: Calculate relative to remote_agents_dir directly
278
+ 2. Flattened cache: Calculate relative to agents_cache_dir directly
277
279
 
278
280
  Example (Git repo):
279
281
  Input: /cache/bobmatnyc/claude-mpm-agents/agents/engineer/backend/python-engineer.md
@@ -281,8 +283,8 @@ class RemoteAgentDiscoveryService:
281
283
  Output: engineer/backend/python-engineer
282
284
 
283
285
  Example (Flattened cache):
284
- Input: /cache/remote-agents/engineer/python-engineer.md
285
- Root: /cache/remote-agents
286
+ Input: /cache/agents/engineer/python-engineer.md
287
+ Root: /cache/agents
286
288
  Output: engineer/python-engineer
287
289
 
288
290
  Args:
@@ -293,7 +295,7 @@ class RemoteAgentDiscoveryService:
293
295
  """
294
296
  try:
295
297
  # Try git repo structure first: /agents/ subdirectory
296
- agents_dir = self.remote_agents_dir / "agents"
298
+ agents_dir = self.agents_cache_dir / "agents"
297
299
  if agents_dir.exists():
298
300
  try:
299
301
  relative_path = file_path.relative_to(agents_dir)
@@ -301,12 +303,12 @@ class RemoteAgentDiscoveryService:
301
303
  except ValueError:
302
304
  pass # Not under agents_dir, try flattened structure
303
305
 
304
- # Try flattened cache structure: calculate relative to remote_agents_dir
306
+ # Try flattened cache structure: calculate relative to agents_cache_dir
305
307
  try:
306
- relative_path = file_path.relative_to(self.remote_agents_dir)
308
+ relative_path = file_path.relative_to(self.agents_cache_dir)
307
309
  return str(relative_path.with_suffix("")).replace("\\", "/")
308
310
  except ValueError:
309
- pass # Not under remote_agents_dir either
311
+ pass # Not under agents_cache_dir either
310
312
 
311
313
  # Fall back to filename
312
314
  self.logger.warning(
@@ -327,7 +329,7 @@ class RemoteAgentDiscoveryService:
327
329
 
328
330
  Supports both cache structures:
329
331
  1. Git repo: Calculate relative to /agents/ subdirectory
330
- 2. Flattened cache: Calculate relative to remote_agents_dir directly
332
+ 2. Flattened cache: Calculate relative to agents_cache_dir directly
331
333
 
332
334
  Example (Git repo):
333
335
  Input: /cache/bobmatnyc/claude-mpm-agents/agents/engineer/backend/python-engineer.md
@@ -335,8 +337,8 @@ class RemoteAgentDiscoveryService:
335
337
  Output: engineer/backend
336
338
 
337
339
  Example (Flattened cache):
338
- Input: /cache/remote-agents/engineer/python-engineer.md
339
- Root: /cache/remote-agents
340
+ Input: /cache/agents/engineer/python-engineer.md
341
+ Root: /cache/agents
340
342
  Output: engineer
341
343
 
342
344
  Args:
@@ -347,7 +349,7 @@ class RemoteAgentDiscoveryService:
347
349
  """
348
350
  try:
349
351
  # Try git repo structure first: /agents/ subdirectory
350
- agents_dir = self.remote_agents_dir / "agents"
352
+ agents_dir = self.agents_cache_dir / "agents"
351
353
  if agents_dir.exists():
352
354
  try:
353
355
  relative_path = file_path.relative_to(agents_dir)
@@ -356,13 +358,13 @@ class RemoteAgentDiscoveryService:
356
358
  except ValueError:
357
359
  pass # Not under agents_dir, try flattened structure
358
360
 
359
- # Try flattened cache structure: calculate relative to remote_agents_dir
361
+ # Try flattened cache structure: calculate relative to agents_cache_dir
360
362
  try:
361
- relative_path = file_path.relative_to(self.remote_agents_dir)
363
+ relative_path = file_path.relative_to(self.agents_cache_dir)
362
364
  parts = relative_path.parts[:-1] # Exclude filename
363
365
  return "/".join(parts) if parts else "universal"
364
366
  except ValueError:
365
- pass # Not under remote_agents_dir either
367
+ pass # Not under agents_cache_dir either
366
368
 
367
369
  return "universal"
368
370
  except Exception:
@@ -385,7 +387,7 @@ class RemoteAgentDiscoveryService:
385
387
  List of agent dictionaries in JSON template format
386
388
 
387
389
  Example:
388
- >>> service = RemoteAgentDiscoveryService(Path("~/.claude-mpm/cache/remote-agents"))
390
+ >>> service = RemoteAgentDiscoveryService(Path("~/.claude-mpm/cache/agents"))
389
391
  >>> agents = service.discover_remote_agents()
390
392
  >>> len(agents)
391
393
  5
@@ -394,9 +396,9 @@ class RemoteAgentDiscoveryService:
394
396
  """
395
397
  agents = []
396
398
 
397
- if not self.remote_agents_dir.exists():
399
+ if not self.agents_cache_dir.exists():
398
400
  self.logger.debug(
399
- f"Remote agents directory does not exist: {self.remote_agents_dir}"
401
+ f"Agents cache directory does not exist: {self.agents_cache_dir}"
400
402
  )
401
403
  return agents
402
404
 
@@ -406,8 +408,8 @@ class RemoteAgentDiscoveryService:
406
408
  # 3. Flattened cache: {path}/ - directly contains category directories (legacy)
407
409
 
408
410
  # Priority 1: Check for dist/agents/ (built output with BASE-AGENT composition)
409
- dist_agents_dir = self.remote_agents_dir / "dist" / "agents"
410
- agents_dir = self.remote_agents_dir / "agents"
411
+ dist_agents_dir = self.agents_cache_dir / "dist" / "agents"
412
+ agents_dir = self.agents_cache_dir / "agents"
411
413
 
412
414
  if dist_agents_dir.exists():
413
415
  # PREFERRED: Use built agents from dist/agents/
@@ -431,18 +433,18 @@ class RemoteAgentDiscoveryService:
431
433
  "documentation",
432
434
  ]
433
435
  has_categories = any(
434
- (self.remote_agents_dir / cat).exists() for cat in category_dirs
436
+ (self.agents_cache_dir / cat).exists() for cat in category_dirs
435
437
  )
436
438
 
437
439
  if has_categories:
438
440
  self.logger.debug(
439
- f"Using flattened cache structure: {self.remote_agents_dir}"
441
+ f"Using flattened cache structure: {self.agents_cache_dir}"
440
442
  )
441
- scan_dir = self.remote_agents_dir
443
+ scan_dir = self.agents_cache_dir
442
444
  else:
443
445
  self.logger.warning(
444
446
  f"No agent directories found. Checked: {dist_agents_dir}, {agents_dir}, "
445
- f"and category directories in {self.remote_agents_dir}. "
447
+ f"and category directories in {self.agents_cache_dir}. "
446
448
  f"Expected agents in /dist/agents/, /agents/, or category directories."
447
449
  )
448
450
  return agents
@@ -482,16 +484,16 @@ class RemoteAgentDiscoveryService:
482
484
 
483
485
  # In flattened cache mode, also exclude files from git repository subdirectories
484
486
  # (files under directories that contain .git folder)
485
- if scan_dir == self.remote_agents_dir:
487
+ if scan_dir == self.agents_cache_dir:
486
488
  filtered_files = []
487
489
  for f in md_files:
488
490
  # Check if this file is inside a git repository (has .git in path)
489
- # Git repos are at {remote_agents_dir}/{owner}/{repo}/.git
490
- path_parts = f.relative_to(self.remote_agents_dir).parts
491
+ # Git repos are at {agents_cache_dir}/{owner}/{repo}/.git
492
+ path_parts = f.relative_to(self.agents_cache_dir).parts
491
493
  if len(path_parts) >= 2:
492
494
  # Check if this looks like a git repo path (owner/repo)
493
495
  potential_repo = (
494
- self.remote_agents_dir / path_parts[0] / path_parts[1]
496
+ self.agents_cache_dir / path_parts[0] / path_parts[1]
495
497
  )
496
498
  if (potential_repo / ".git").exists():
497
499
  # This file is in a git repo, skip it (we'll handle git repos separately)
@@ -518,7 +520,7 @@ class RemoteAgentDiscoveryService:
518
520
  self.logger.warning(f"Failed to parse remote agent {md_file.name}: {e}")
519
521
 
520
522
  self.logger.info(
521
- f"Discovered {len(agents)} remote agents from {self.remote_agents_dir.name}"
523
+ f"Discovered {len(agents)} remote agents from {self.agents_cache_dir.name}"
522
524
  )
523
525
  return agents
524
526
 
@@ -735,7 +737,7 @@ class RemoteAgentDiscoveryService:
735
737
  RemoteAgentMetadata if found, None otherwise
736
738
  """
737
739
  # Bug #4 fix: Search in /agents/ subdirectory, not root directory
738
- agents_dir = self.remote_agents_dir / "agents"
740
+ agents_dir = self.agents_cache_dir / "agents"
739
741
  if not agents_dir.exists():
740
742
  return None
741
743
 
@@ -767,7 +769,7 @@ class RemoteAgentDiscoveryService:
767
769
  List of agent dictionaries from the specified collection
768
770
 
769
771
  Example:
770
- >>> service = RemoteAgentDiscoveryService(Path("~/.claude-mpm/cache/remote-agents"))
772
+ >>> service = RemoteAgentDiscoveryService(Path("~/.claude-mpm/cache/agents"))
771
773
  >>> agents = service.get_agents_by_collection("bobmatnyc/claude-mpm-agents")
772
774
  >>> len(agents)
773
775
  45
@@ -795,7 +797,7 @@ class RemoteAgentDiscoveryService:
795
797
  - agents: List of agent IDs in collection
796
798
 
797
799
  Example:
798
- >>> service = RemoteAgentDiscoveryService(Path("~/.claude-mpm/cache/remote-agents"))
800
+ >>> service = RemoteAgentDiscoveryService(Path("~/.claude-mpm/cache/agents"))
799
801
  >>> collections = service.list_collections()
800
802
  >>> collections
801
803
  [
@@ -50,10 +50,10 @@ class GitSourceManager:
50
50
 
51
51
  Args:
52
52
  cache_root: Root directory for repository caches.
53
- Defaults to ~/.claude-mpm/cache/remote-agents/
53
+ Defaults to ~/.claude-mpm/cache/agents/
54
54
  """
55
55
  if cache_root is None:
56
- cache_root = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
56
+ cache_root = Path.home() / ".claude-mpm" / "cache" / "agents"
57
57
 
58
58
  self.cache_root = cache_root
59
59
  self.cache_root.mkdir(parents=True, exist_ok=True)
@@ -226,9 +226,11 @@ class AgentRecommenderService(BaseService, IAgentRecommender):
226
226
  if max_agents is not None:
227
227
  recommendations = recommendations[:max_agents]
228
228
 
229
- # Check if toolchain is unknown and we have no recommendations
230
- if not recommendations and toolchain.primary_language.lower() == "unknown":
231
- self.logger.info("Toolchain unknown - applying default configuration")
229
+ # Check if we have no recommendations (any reason: unknown language, low scores, etc.)
230
+ if not recommendations:
231
+ self.logger.info(
232
+ f"No agents scored above threshold for {toolchain.primary_language}; using defaults"
233
+ )
232
234
 
233
235
  # Get default configuration
234
236
  default_config = self._capabilities_config.get("default_configuration", {})
@@ -78,14 +78,14 @@ class SingleTierDeploymentService:
78
78
  config: Agent source configuration with repositories
79
79
  deployment_dir: Target deployment directory (.claude/agents/)
80
80
  cache_root: Cache root for repositories
81
- (defaults to ~/.claude-mpm/cache/remote-agents/)
81
+ (defaults to ~/.claude-mpm/cache/agents/)
82
82
  """
83
83
  self.config = config
84
84
  self.deployment_dir = deployment_dir
85
85
  self.deployment_dir.mkdir(parents=True, exist_ok=True)
86
86
 
87
87
  if cache_root is None:
88
- cache_root = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
88
+ cache_root = Path.home() / ".claude-mpm" / "cache" / "agents"
89
89
 
90
90
  self.cache_root = cache_root
91
91
  self.git_source_manager = GitSourceManager(cache_root)
@@ -188,10 +188,10 @@ class GitSourceSyncService:
188
188
 
189
189
  Args:
190
190
  source_url: Base URL for raw files (without trailing slash)
191
- cache_dir: Local cache directory (defaults to ~/.claude-mpm/cache/remote-agents/)
191
+ cache_dir: Local cache directory (defaults to ~/.claude-mpm/cache/agents/)
192
192
  source_id: Unique identifier for this source (for multi-source support)
193
193
 
194
- Design Decision: Cache to ~/.claude-mpm/cache/remote-agents/ (canonical location)
194
+ Design Decision: Cache to ~/.claude-mpm/cache/agents/ (canonical location)
195
195
 
196
196
  Rationale: Separates cached repository structure from deployed agents.
197
197
  This allows preserving nested directory structure in cache while
@@ -207,13 +207,13 @@ class GitSourceSyncService:
207
207
  self.source_url = source_url.rstrip("/")
208
208
  self.source_id = source_id
209
209
 
210
- # Setup cache directory (canonical: ~/.claude-mpm/cache/remote-agents/)
210
+ # Setup cache directory (canonical: ~/.claude-mpm/cache/agents/)
211
211
  if cache_dir:
212
212
  self.cache_dir = Path(cache_dir)
213
213
  else:
214
- # Default to ~/.claude-mpm/cache/remote-agents/ (canonical cache location)
214
+ # Default to ~/.claude-mpm/cache/agents/ (canonical cache location)
215
215
  home = Path.home()
216
- self.cache_dir = home / ".claude-mpm" / "cache" / "remote-agents"
216
+ self.cache_dir = home / ".claude-mpm" / "cache" / "agents"
217
217
 
218
218
  self.cache_dir.mkdir(parents=True, exist_ok=True)
219
219
 
@@ -110,6 +110,26 @@ def sync_agents_on_startup(config: Optional[Dict[str, Any]] = None) -> Dict[str,
110
110
  else:
111
111
  cache_dir = None # Will use default
112
112
 
113
+ # Check for old cache directory names and provide migration guidance
114
+ # This handles users upgrading from older versions
115
+ old_cache_paths = [
116
+ Path.home() / ".claude-mpm" / "cache" / "remote-agents",
117
+ ]
118
+ new_cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
119
+
120
+ for old_cache in old_cache_paths:
121
+ if old_cache.exists() and not new_cache_dir.exists():
122
+ logger.warning(f"Found old cache directory: {old_cache}")
123
+ logger.warning(
124
+ "The cache directory location has changed to: ~/.claude-mpm/cache/agents"
125
+ )
126
+ logger.warning("To migrate your existing cache, run:")
127
+ logger.warning(f" mv {old_cache} {new_cache_dir}")
128
+ logger.info(
129
+ "Agents will be re-synced to the new cache location automatically."
130
+ )
131
+ break # Only show warning once
132
+
113
133
  # Sync each enabled source
114
134
  for source_config in sources:
115
135
  try:
@@ -217,7 +237,7 @@ def get_sync_status() -> Dict[str, Any]:
217
237
  "enabled": agent_sync_config.get("enabled", True),
218
238
  "sources_configured": len(enabled_sources),
219
239
  "cache_dir": agent_sync_config.get(
220
- "cache_dir", "~/.claude-mpm/cache/remote-agents"
240
+ "cache_dir", "~/.claude-mpm/cache/agents"
221
241
  ),
222
242
  }
223
243
 
@@ -233,7 +253,7 @@ def get_sync_status() -> Dict[str, Any]:
233
253
  return {
234
254
  "enabled": False,
235
255
  "sources_configured": 0,
236
- "cache_dir": "~/.claude-mpm/cache/remote-agents",
256
+ "cache_dir": "~/.claude-mpm/cache/agents",
237
257
  "last_sync": None,
238
258
  "error": str(e),
239
259
  }
@@ -66,9 +66,9 @@ class AgentCheck(BaseDiagnosticCheck):
66
66
 
67
67
  if deployed_count == 0:
68
68
  status = ValidationSeverity.ERROR
69
- message = f"No agents deployed (0/{available_count} available)"
69
+ message = f"No agents deployed (0/{available_count} cached)"
70
70
  fix_command = "claude-mpm agents deploy"
71
- fix_description = "Deploy all available agents"
71
+ fix_description = "Deploy all cached agents"
72
72
  elif deployed_count < available_count:
73
73
  status = ValidationSeverity.WARNING
74
74
  message = f"{deployed_count}/{available_count} agents deployed"
@@ -432,7 +432,7 @@ class AgentSourcesCheck(BaseDiagnosticCheck):
432
432
 
433
433
  def _check_cache_directory(self) -> DiagnosticResult:
434
434
  """Check cache directory health."""
435
- cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
435
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
436
436
 
437
437
  if not cache_dir.exists():
438
438
  return DiagnosticResult(
@@ -12,10 +12,10 @@ Design Decisions:
12
12
 
13
13
  Example:
14
14
  >>> service = GitOperationsService()
15
- >>> success = service.create_branch(Path("~/.claude-mpm/cache/remote-agents"), "improve/research-memory")
15
+ >>> success = service.create_branch(Path("~/.claude-mpm/cache/agents"), "improve/research-memory")
16
16
  >>> if success:
17
- ... service.stage_files(Path("~/.claude-mpm/cache/remote-agents"), ["agents/research.md"])
18
- ... service.commit(Path("~/.claude-mpm/cache/remote-agents"), "feat: improve research agent memory handling")
17
+ ... service.stage_files(Path("~/.claude-mpm/cache/agents"), ["agents/research.md"])
18
+ ... service.commit(Path("~/.claude-mpm/cache/agents"), "feat: improve research agent memory handling")
19
19
  """
20
20
 
21
21
  import logging
@@ -68,7 +68,7 @@ class GitOperationsService:
68
68
 
69
69
  Example:
70
70
  >>> service = GitOperationsService()
71
- >>> service.is_git_repo(Path("~/.claude-mpm/cache/remote-agents"))
71
+ >>> service.is_git_repo(Path("~/.claude-mpm/cache/agents"))
72
72
  True
73
73
  """
74
74
  try:
@@ -150,7 +150,7 @@ class GitOperationsService:
150
150
  Example:
151
151
  >>> service = GitOperationsService()
152
152
  >>> service.create_and_checkout_branch(
153
- ... Path("~/.claude-mpm/cache/remote-agents"),
153
+ ... Path("~/.claude-mpm/cache/agents"),
154
154
  ... "improve/research-memory",
155
155
  ... "main"
156
156
  ... )
@@ -245,7 +245,7 @@ class GitOperationsService:
245
245
  Example:
246
246
  >>> service = GitOperationsService()
247
247
  >>> service.commit(
248
- ... Path("~/.claude-mpm/cache/remote-agents"),
248
+ ... Path("~/.claude-mpm/cache/agents"),
249
249
  ... "feat(agent): improve research agent memory handling\\n\\n- Add hard limit of 5 files"
250
250
  ... )
251
251
  True
@@ -289,7 +289,7 @@ class GitOperationsService:
289
289
 
290
290
  Example:
291
291
  >>> service = GitOperationsService()
292
- >>> service.push(Path("~/.claude-mpm/cache/remote-agents"), "improve/research-memory")
292
+ >>> service.push(Path("~/.claude-mpm/cache/agents"), "improve/research-memory")
293
293
  True
294
294
  """
295
295
  self._validate_repo(repo_path)
@@ -489,7 +489,7 @@ class GitOperationsService:
489
489
 
490
490
  Example:
491
491
  >>> service = GitOperationsService()
492
- >>> valid, msg = service.validate_repo(Path("~/.claude-mpm/cache/remote-agents"))
492
+ >>> valid, msg = service.validate_repo(Path("~/.claude-mpm/cache/agents"))
493
493
  >>> if not valid:
494
494
  ... print(f"Repository invalid: {msg}")
495
495
  """