claude-mpm 3.5.2__py3-none-any.whl → 3.5.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. claude_mpm/.claude-mpm/logs/hooks_20250728.log +10 -0
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/agents/INSTRUCTIONS.md +14 -13
  4. claude_mpm/agents/agent-template.yaml +83 -0
  5. claude_mpm/agents/agent_loader.py +109 -15
  6. claude_mpm/agents/base_agent.json +1 -1
  7. claude_mpm/agents/frontmatter_validator.py +448 -0
  8. claude_mpm/agents/templates/data_engineer.json +4 -3
  9. claude_mpm/agents/templates/documentation.json +4 -3
  10. claude_mpm/agents/templates/engineer.json +4 -3
  11. claude_mpm/agents/templates/ops.json +4 -3
  12. claude_mpm/agents/templates/pm.json +5 -4
  13. claude_mpm/agents/templates/qa.json +4 -3
  14. claude_mpm/agents/templates/research.json +8 -7
  15. claude_mpm/agents/templates/security.json +4 -3
  16. claude_mpm/agents/templates/test_integration.json +4 -3
  17. claude_mpm/agents/templates/version_control.json +4 -3
  18. claude_mpm/cli/README.md +108 -0
  19. claude_mpm/cli/commands/agents.py +373 -7
  20. claude_mpm/cli/commands/run.py +4 -11
  21. claude_mpm/cli/parser.py +36 -0
  22. claude_mpm/cli/utils.py +9 -1
  23. claude_mpm/cli_module/refactoring_guide.md +253 -0
  24. claude_mpm/config/async_logging_config.yaml +145 -0
  25. claude_mpm/constants.py +2 -0
  26. claude_mpm/core/.claude-mpm/logs/hooks_20250730.log +34 -0
  27. claude_mpm/core/agent_registry.py +4 -1
  28. claude_mpm/core/claude_runner.py +297 -20
  29. claude_mpm/core/config_paths.py +0 -1
  30. claude_mpm/core/factories.py +9 -3
  31. claude_mpm/dashboard/.claude-mpm/memories/README.md +36 -0
  32. claude_mpm/dashboard/README.md +121 -0
  33. claude_mpm/dashboard/static/js/dashboard.js.backup +1973 -0
  34. claude_mpm/dashboard/templates/.claude-mpm/memories/README.md +36 -0
  35. claude_mpm/dashboard/templates/.claude-mpm/memories/engineer_agent.md +39 -0
  36. claude_mpm/dashboard/templates/.claude-mpm/memories/version_control_agent.md +38 -0
  37. claude_mpm/hooks/README.md +96 -0
  38. claude_mpm/init.py +83 -13
  39. claude_mpm/schemas/agent_schema.json +435 -0
  40. claude_mpm/services/agents/deployment/agent_deployment.py +204 -18
  41. claude_mpm/services/agents/management/agent_management_service.py +2 -1
  42. claude_mpm/services/agents/registry/agent_registry.py +22 -1
  43. claude_mpm/services/framework_claude_md_generator/README.md +92 -0
  44. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +3 -3
  45. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +2 -2
  46. claude_mpm/services/version_control/VERSION +1 -0
  47. claude_mpm/validation/agent_validator.py +56 -1
  48. {claude_mpm-3.5.2.dist-info → claude_mpm-3.5.6.dist-info}/METADATA +60 -3
  49. {claude_mpm-3.5.2.dist-info → claude_mpm-3.5.6.dist-info}/RECORD +53 -36
  50. {claude_mpm-3.5.2.dist-info → claude_mpm-3.5.6.dist-info}/WHEEL +0 -0
  51. {claude_mpm-3.5.2.dist-info → claude_mpm-3.5.6.dist-info}/entry_points.txt +0 -0
  52. {claude_mpm-3.5.2.dist-info → claude_mpm-3.5.6.dist-info}/licenses/LICENSE +0 -0
  53. {claude_mpm-3.5.2.dist-info → claude_mpm-3.5.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,108 @@
1
+ # Claude MPM CLI Architecture
2
+
3
+ This document describes the refactored CLI architecture for claude-mpm.
4
+
5
+ ## Overview
6
+
7
+ The CLI has been refactored from a single monolithic `cli.py` file into a modular structure that improves maintainability and code organization.
8
+
9
+ ## Directory Structure
10
+
11
+ ```
12
+ cli/
13
+ ├── __init__.py # Main entry point - orchestrates the CLI flow
14
+ ├── parser.py # Argument parsing logic - single source of truth for CLI arguments
15
+ ├── utils.py # Shared utility functions
16
+ ├── commands/ # Individual command implementations
17
+ │ ├── __init__.py
18
+ │ ├── run.py # Default command - runs Claude sessions
19
+ │ ├── tickets.py # Lists tickets
20
+ │ ├── info.py # Shows system information
21
+ │ └── agents.py # Manages agent deployments
22
+ └── README.md # This file
23
+ ```
24
+
25
+ ## Key Design Decisions
26
+
27
+ ### 1. Modular Command Structure
28
+ Each command is implemented in its own module under `commands/`. This makes it easy to:
29
+ - Add new commands without touching existing code
30
+ - Test commands in isolation
31
+ - Understand what each command does
32
+
33
+ ### 2. Centralized Argument Parsing
34
+ All argument definitions are in `parser.py`. This provides:
35
+ - Single source of truth for CLI arguments
36
+ - Reusable argument groups (common arguments, run arguments)
37
+ - Clear separation of parsing from execution
38
+
39
+ ### 3. Shared Utilities
40
+ Common functions are in `utils.py`:
41
+ - `get_user_input()` - Handles input from files, stdin, or command line
42
+ - `get_agent_versions_display()` - Formats agent version information
43
+ - `setup_logging()` - Configures logging based on arguments
44
+ - `ensure_directories()` - Creates required directories on first run
45
+
46
+ ### 4. Backward Compatibility
47
+ The refactoring maintains full backward compatibility:
48
+ - `__main__.py` still imports from `claude_mpm.cli`
49
+ - The main `cli/__init__.py` exports the same `main()` function
50
+ - All existing commands and arguments work exactly as before
51
+
52
+ ## Entry Points
53
+
54
+ 1. **Package execution**: `python -m claude_mpm`
55
+ - Uses `__main__.py` which imports from `cli/__init__.py`
56
+
57
+ 2. **Direct import**: `from claude_mpm.cli import main`
58
+ - Imports the main function from `cli/__init__.py`
59
+
60
+ 3. **Shell script**: `claude-mpm` command
61
+ - Calls `python -m claude_mpm` with proper environment setup
62
+
63
+ ## Adding New Commands
64
+
65
+ To add a new command:
66
+
67
+ 1. Create a new module in `commands/`:
68
+ ```python
69
+ # commands/mycommand.py
70
+ def my_command(args):
71
+ """Execute my command."""
72
+ # Implementation here
73
+ ```
74
+
75
+ 2. Add the command to `commands/__init__.py`:
76
+ ```python
77
+ from .mycommand import my_command
78
+ ```
79
+
80
+ 3. Add parser configuration in `parser.py`:
81
+ ```python
82
+ # In create_parser()
83
+ mycommand_parser = subparsers.add_parser(
84
+ "mycommand",
85
+ help="Description of my command"
86
+ )
87
+ # Add command-specific arguments
88
+ ```
89
+
90
+ 4. Add the command mapping in `cli/__init__.py`:
91
+ ```python
92
+ # In _execute_command()
93
+ command_map = {
94
+ # ... existing commands ...
95
+ "mycommand": my_command,
96
+ }
97
+ ```
98
+
99
+ ## Removed Files
100
+
101
+ - `cli_main.py` - Redundant entry point, functionality moved to `__main__.py`
102
+ - Original `cli.py` - Split into the modular structure described above
103
+
104
+ ## Preserved Files
105
+
106
+ - `cli_enhancements.py` - Experimental Click-based CLI with enhanced features
107
+ - Kept for reference and future enhancement ideas
108
+ - Not currently used in production
@@ -6,10 +6,15 @@ and cleaning agent deployments.
6
6
  """
7
7
 
8
8
  from pathlib import Path
9
+ import json
10
+ import yaml
11
+ from typing import Dict, Any, Optional
9
12
 
10
13
  from ...core.logger import get_logger
11
14
  from ...constants import AgentCommands
12
15
  from ..utils import get_agent_versions_display
16
+ from ...core.agent_registry import AgentRegistryAdapter
17
+ from ...agents.frontmatter_validator import FrontmatterValidator
13
18
 
14
19
 
15
20
  def manage_agents(args):
@@ -29,7 +34,16 @@ def manage_agents(args):
29
34
 
30
35
  try:
31
36
  from ...services import AgentDeploymentService
32
- deployment_service = AgentDeploymentService()
37
+ import os
38
+ from pathlib import Path
39
+
40
+ # Determine the user's working directory from environment
41
+ # This ensures agents are deployed to the correct directory
42
+ user_working_dir = None
43
+ if 'CLAUDE_MPM_USER_PWD' in os.environ:
44
+ user_working_dir = Path(os.environ['CLAUDE_MPM_USER_PWD'])
45
+
46
+ deployment_service = AgentDeploymentService(working_directory=user_working_dir)
33
47
 
34
48
  if not args.agents_command:
35
49
  # No subcommand - show agent versions
@@ -55,6 +69,12 @@ def manage_agents(args):
55
69
  elif args.agents_command == AgentCommands.CLEAN.value:
56
70
  _clean_agents(args, deployment_service)
57
71
 
72
+ elif args.agents_command == AgentCommands.VIEW.value:
73
+ _view_agent(args)
74
+
75
+ elif args.agents_command == AgentCommands.FIX.value:
76
+ _fix_agents(args)
77
+
58
78
  except ImportError:
59
79
  logger.error("Agent deployment service not available")
60
80
  print("Error: Agent deployment service not available")
@@ -71,10 +91,13 @@ def _list_agents(args, deployment_service):
71
91
  currently deployed. This helps them understand the agent ecosystem.
72
92
 
73
93
  Args:
74
- args: Command arguments with 'system' and 'deployed' flags
94
+ args: Command arguments with 'system', 'deployed', and 'by_tier' flags
75
95
  deployment_service: Agent deployment service instance
76
96
  """
77
- if args.system:
97
+ if hasattr(args, 'by_tier') and args.by_tier:
98
+ # List agents grouped by tier
99
+ _list_agents_by_tier()
100
+ elif args.system:
78
101
  # List available agent templates
79
102
  print("Available Agent Templates:")
80
103
  print("-" * 80)
@@ -114,21 +137,22 @@ def _list_agents(args, deployment_service):
114
137
 
115
138
  else:
116
139
  # Default: show usage
117
- print("Use --system to list system agents or --deployed to list deployed agents")
140
+ print("Use --system to list system agents, --deployed to list deployed agents, or --by-tier to group by precedence")
118
141
 
119
142
 
120
143
  def _deploy_agents(args, deployment_service, force=False):
121
144
  """
122
- Deploy system agents.
145
+ Deploy both system and project agents.
123
146
 
124
147
  WHY: Agents need to be deployed to the working directory for Claude Code to use them.
125
- This function handles both regular and forced deployment.
148
+ This function handles both regular and forced deployment, including project-specific agents.
126
149
 
127
150
  Args:
128
151
  args: Command arguments with optional 'target' path
129
152
  deployment_service: Agent deployment service instance
130
153
  force: Whether to force rebuild all agents
131
154
  """
155
+ # Deploy system agents first
132
156
  if force:
133
157
  print("Force deploying all system agents...")
134
158
  else:
@@ -136,6 +160,43 @@ def _deploy_agents(args, deployment_service, force=False):
136
160
 
137
161
  results = deployment_service.deploy_agents(args.target, force_rebuild=force)
138
162
 
163
+ # Also deploy project agents if they exist
164
+ from pathlib import Path
165
+ import os
166
+
167
+ # Use the user's working directory if available
168
+ if 'CLAUDE_MPM_USER_PWD' in os.environ:
169
+ project_dir = Path(os.environ['CLAUDE_MPM_USER_PWD'])
170
+ else:
171
+ project_dir = Path.cwd()
172
+
173
+ project_agents_dir = project_dir / '.claude-mpm' / 'agents'
174
+ if project_agents_dir.exists():
175
+ json_files = list(project_agents_dir.glob('*.json'))
176
+ if json_files:
177
+ print(f"\nDeploying {len(json_files)} project agents...")
178
+ from claude_mpm.services.agents.deployment.agent_deployment import AgentDeploymentService
179
+ project_service = AgentDeploymentService(
180
+ templates_dir=project_agents_dir,
181
+ base_agent_path=project_agents_dir / 'base_agent.json' if (project_agents_dir / 'base_agent.json').exists() else None,
182
+ working_directory=project_dir # Pass the project directory
183
+ )
184
+ project_results = project_service.deploy_agents(
185
+ target_dir=args.target if args.target else Path.cwd() / '.claude' / 'agents',
186
+ force_rebuild=force,
187
+ deployment_mode='project'
188
+ )
189
+
190
+ # Merge project results into main results
191
+ if project_results.get('deployed'):
192
+ results['deployed'].extend(project_results['deployed'])
193
+ print(f"✓ Deployed {len(project_results['deployed'])} project agents")
194
+ if project_results.get('updated'):
195
+ results['updated'].extend(project_results['updated'])
196
+ print(f"✓ Updated {len(project_results['updated'])} project agents")
197
+ if project_results.get('errors'):
198
+ results['errors'].extend(project_results['errors'])
199
+
139
200
  if results["deployed"]:
140
201
  print(f"\n✓ Successfully deployed {len(results['deployed'])} agents to {results['target_dir']}")
141
202
  for agent in results["deployed"]:
@@ -188,4 +249,309 @@ def _clean_agents(args, deployment_service):
188
249
  if results["errors"]:
189
250
  print("\n❌ Errors during cleanup:")
190
251
  for error in results["errors"]:
191
- print(f" - {error}")
252
+ print(f" - {error}")
253
+
254
+
255
+ def _list_agents_by_tier():
256
+ """
257
+ List agents grouped by precedence tier.
258
+
259
+ WHY: Users need to understand which agents are active across different tiers
260
+ and which version takes precedence when multiple versions exist.
261
+ """
262
+ try:
263
+ adapter = AgentRegistryAdapter()
264
+ if not adapter.registry:
265
+ print("❌ Could not initialize agent registry")
266
+ return
267
+
268
+ # Get all agents and group by tier
269
+ all_agents = adapter.registry.list_agents()
270
+
271
+ # Group agents by tier and name
272
+ tiers = {'project': {}, 'user': {}, 'system': {}}
273
+ agent_names = set()
274
+
275
+ for agent_id, metadata in all_agents.items():
276
+ tier = metadata.get('tier', 'system')
277
+ if tier in tiers:
278
+ tiers[tier][agent_id] = metadata
279
+ agent_names.add(agent_id)
280
+
281
+ # Display header
282
+ print("\n" + "=" * 80)
283
+ print(" " * 25 + "AGENT HIERARCHY BY TIER")
284
+ print("=" * 80)
285
+ print("\nPrecedence: PROJECT > USER > SYSTEM")
286
+ print("(Agents in higher tiers override those in lower tiers)\n")
287
+
288
+ # Display each tier
289
+ tier_order = [('PROJECT', 'project'), ('USER', 'user'), ('SYSTEM', 'system')]
290
+
291
+ for tier_display, tier_key in tier_order:
292
+ agents = tiers[tier_key]
293
+ print(f"\n{'─' * 35} {tier_display} TIER {'─' * 35}")
294
+
295
+ if not agents:
296
+ print(f" No agents at {tier_key} level")
297
+ else:
298
+ # Check paths to determine actual locations
299
+ if tier_key == 'project':
300
+ print(f" Location: .claude-mpm/agents/ (in current project)")
301
+ elif tier_key == 'user':
302
+ print(f" Location: ~/.claude-mpm/agents/")
303
+ else:
304
+ print(f" Location: Built-in framework agents")
305
+
306
+ print(f"\n Found {len(agents)} agent(s):\n")
307
+
308
+ for agent_id, metadata in sorted(agents.items()):
309
+ # Check if this agent is overridden by higher tiers
310
+ is_active = True
311
+ overridden_by = []
312
+
313
+ for check_tier_display, check_tier_key in tier_order:
314
+ if check_tier_key == tier_key:
315
+ break
316
+ if agent_id in tiers[check_tier_key]:
317
+ is_active = False
318
+ overridden_by.append(check_tier_display)
319
+
320
+ # Display agent info
321
+ status = "✓ ACTIVE" if is_active else f"⊗ OVERRIDDEN by {', '.join(overridden_by)}"
322
+ print(f" 📄 {agent_id:<20} [{status}]")
323
+
324
+ # Show metadata
325
+ if 'description' in metadata:
326
+ print(f" Description: {metadata['description']}")
327
+ if 'path' in metadata:
328
+ path = Path(metadata['path'])
329
+ print(f" File: {path.name}")
330
+ print()
331
+
332
+ # Summary
333
+ print("\n" + "=" * 80)
334
+ print("SUMMARY:")
335
+ print(f" Total unique agents: {len(agent_names)}")
336
+ print(f" Project agents: {len(tiers['project'])}")
337
+ print(f" User agents: {len(tiers['user'])}")
338
+ print(f" System agents: {len(tiers['system'])}")
339
+ print("=" * 80 + "\n")
340
+
341
+ except Exception as e:
342
+ print(f"❌ Error listing agents by tier: {e}")
343
+
344
+
345
+ def _view_agent(args):
346
+ """
347
+ View detailed information about a specific agent.
348
+
349
+ WHY: Users need to inspect agent configurations, frontmatter, and instructions
350
+ to understand what an agent does and how it's configured.
351
+
352
+ Args:
353
+ args: Command arguments with 'agent_name' attribute
354
+ """
355
+ if not hasattr(args, 'agent_name') or not args.agent_name:
356
+ print("❌ Please specify an agent name to view")
357
+ print("Usage: claude-mpm agents view <agent_name>")
358
+ return
359
+
360
+ try:
361
+ adapter = AgentRegistryAdapter()
362
+ if not adapter.registry:
363
+ print("❌ Could not initialize agent registry")
364
+ return
365
+
366
+ # Get the agent
367
+ agent = adapter.registry.get_agent(args.agent_name)
368
+ if not agent:
369
+ print(f"❌ Agent '{args.agent_name}' not found")
370
+ print("\nAvailable agents:")
371
+ all_agents = adapter.registry.list_agents()
372
+ for agent_id in sorted(all_agents.keys()):
373
+ print(f" - {agent_id}")
374
+ return
375
+
376
+ # Read the agent file
377
+ agent_path = Path(agent.path)
378
+ if not agent_path.exists():
379
+ print(f"❌ Agent file not found: {agent_path}")
380
+ return
381
+
382
+ with open(agent_path, 'r') as f:
383
+ content = f.read()
384
+
385
+ # Display agent information
386
+ print("\n" + "=" * 80)
387
+ print(f" AGENT: {agent.name}")
388
+ print("=" * 80)
389
+
390
+ # Basic info
391
+ print(f"\n📋 BASIC INFORMATION:")
392
+ print(f" Name: {agent.name}")
393
+ print(f" Type: {agent.type}")
394
+ print(f" Tier: {agent.tier.upper()}")
395
+ print(f" Path: {agent_path}")
396
+ if agent.description:
397
+ print(f" Description: {agent.description}")
398
+ if agent.specializations:
399
+ print(f" Specializations: {', '.join(agent.specializations)}")
400
+
401
+ # Extract and display frontmatter
402
+ if content.startswith("---"):
403
+ try:
404
+ end_marker = content.find("\n---\n", 4)
405
+ if end_marker == -1:
406
+ end_marker = content.find("\n---\r\n", 4)
407
+
408
+ if end_marker != -1:
409
+ frontmatter_str = content[4:end_marker]
410
+ frontmatter = yaml.safe_load(frontmatter_str)
411
+
412
+ print(f"\n📝 FRONTMATTER:")
413
+ for key, value in frontmatter.items():
414
+ if isinstance(value, list):
415
+ print(f" {key}: [{', '.join(str(v) for v in value)}]")
416
+ elif isinstance(value, dict):
417
+ print(f" {key}:")
418
+ for k, v in value.items():
419
+ print(f" {k}: {v}")
420
+ else:
421
+ print(f" {key}: {value}")
422
+
423
+ # Extract instructions preview
424
+ instructions_start = end_marker + 5
425
+ instructions = content[instructions_start:].strip()
426
+
427
+ if instructions:
428
+ print(f"\n📖 INSTRUCTIONS PREVIEW (first 500 chars):")
429
+ print(" " + "-" * 76)
430
+ preview = instructions[:500]
431
+ if len(instructions) > 500:
432
+ preview += "...\n\n [Truncated - {:.1f}KB total]".format(len(instructions) / 1024)
433
+
434
+ for line in preview.split('\n'):
435
+ print(f" {line}")
436
+ print(" " + "-" * 76)
437
+ except Exception as e:
438
+ print(f"\n⚠️ Could not parse frontmatter: {e}")
439
+ else:
440
+ print(f"\n⚠️ No frontmatter found in agent file")
441
+
442
+ # File stats
443
+ import os
444
+ stat = os.stat(agent_path)
445
+ from datetime import datetime
446
+ modified = datetime.fromtimestamp(stat.st_mtime).strftime("%Y-%m-%d %H:%M:%S")
447
+ print(f"\n📊 FILE STATS:")
448
+ print(f" Size: {stat.st_size:,} bytes")
449
+ print(f" Last modified: {modified}")
450
+
451
+ print("\n" + "=" * 80 + "\n")
452
+
453
+ except Exception as e:
454
+ print(f"❌ Error viewing agent: {e}")
455
+
456
+
457
+ def _fix_agents(args):
458
+ """
459
+ Fix agent frontmatter issues using FrontmatterValidator.
460
+
461
+ WHY: Agent files may have formatting issues in their frontmatter that prevent
462
+ proper loading. This command automatically fixes common issues.
463
+
464
+ Args:
465
+ args: Command arguments with 'agent_name', 'dry_run', and 'all' flags
466
+ """
467
+ validator = FrontmatterValidator()
468
+
469
+ try:
470
+ adapter = AgentRegistryAdapter()
471
+ if not adapter.registry:
472
+ print("❌ Could not initialize agent registry")
473
+ return
474
+
475
+ # Determine which agents to fix
476
+ agents_to_fix = []
477
+
478
+ if hasattr(args, 'all') and args.all:
479
+ # Fix all agents
480
+ all_agents = adapter.registry.list_agents()
481
+ for agent_id, metadata in all_agents.items():
482
+ agents_to_fix.append((agent_id, metadata['path']))
483
+ print(f"\n🔧 Checking {len(agents_to_fix)} agent(s) for frontmatter issues...\n")
484
+ elif hasattr(args, 'agent_name') and args.agent_name:
485
+ # Fix specific agent
486
+ agent = adapter.registry.get_agent(args.agent_name)
487
+ if not agent:
488
+ print(f"❌ Agent '{args.agent_name}' not found")
489
+ return
490
+ agents_to_fix.append((agent.name, agent.path))
491
+ print(f"\n🔧 Checking agent '{agent.name}' for frontmatter issues...\n")
492
+ else:
493
+ print("❌ Please specify an agent name or use --all to fix all agents")
494
+ print("Usage: claude-mpm agents fix [agent_name] [--dry-run] [--all]")
495
+ return
496
+
497
+ dry_run = hasattr(args, 'dry_run') and args.dry_run
498
+ if dry_run:
499
+ print("🔍 DRY RUN MODE - No changes will be made\n")
500
+
501
+ # Process each agent
502
+ total_issues = 0
503
+ total_fixed = 0
504
+
505
+ for agent_name, agent_path in agents_to_fix:
506
+ path = Path(agent_path)
507
+ if not path.exists():
508
+ print(f"⚠️ Skipping {agent_name}: File not found at {path}")
509
+ continue
510
+
511
+ print(f"📄 {agent_name}:")
512
+
513
+ # Validate and potentially fix
514
+ result = validator.correct_file(path, dry_run=dry_run)
515
+
516
+ if result.is_valid and not result.corrections:
517
+ print(" ✓ No issues found")
518
+ else:
519
+ if result.errors:
520
+ print(" ❌ Errors:")
521
+ for error in result.errors:
522
+ print(f" - {error}")
523
+ total_issues += len(result.errors)
524
+
525
+ if result.warnings:
526
+ print(" ⚠️ Warnings:")
527
+ for warning in result.warnings:
528
+ print(f" - {warning}")
529
+ total_issues += len(result.warnings)
530
+
531
+ if result.corrections:
532
+ if dry_run:
533
+ print(" 🔧 Would fix:")
534
+ else:
535
+ print(" ✓ Fixed:")
536
+ total_fixed += len(result.corrections)
537
+ for correction in result.corrections:
538
+ print(f" - {correction}")
539
+
540
+ print()
541
+
542
+ # Summary
543
+ print("=" * 80)
544
+ print("SUMMARY:")
545
+ print(f" Agents checked: {len(agents_to_fix)}")
546
+ print(f" Total issues found: {total_issues}")
547
+ if dry_run:
548
+ print(f" Issues that would be fixed: {sum(1 for _, path in agents_to_fix if validator.validate_file(Path(path)).corrections)}")
549
+ print("\n💡 Run without --dry-run to apply fixes")
550
+ else:
551
+ print(f" Issues fixed: {total_fixed}")
552
+ if total_fixed > 0:
553
+ print("\n✓ Frontmatter issues have been fixed!")
554
+ print("=" * 80 + "\n")
555
+
556
+ except Exception as e:
557
+ print(f"❌ Error fixing agents: {e}")
@@ -281,17 +281,10 @@ def run_session(args):
281
281
  websocket_port=websocket_port
282
282
  )
283
283
 
284
- # Ensure project agents are available if we're in a project directory
285
- # This deploys system agents to .claude-mpm/agents/ for local customization
286
- if not hasattr(args, 'no_native_agents') or not args.no_native_agents:
287
- # Check if we're in a project directory (has .git or other markers)
288
- project_markers = ['.git', 'pyproject.toml', 'package.json', 'requirements.txt']
289
- cwd = Path.cwd()
290
- is_project = any((cwd / marker).exists() for marker in project_markers)
291
-
292
- if is_project:
293
- logger.debug("Detected project directory, ensuring agents are available locally")
294
- runner.ensure_project_agents()
284
+ # Agent deployment is handled by ClaudeRunner.setup_agents() and
285
+ # ClaudeRunner.deploy_project_agents_to_claude() which are called
286
+ # in both run_interactive() and run_oneshot() methods.
287
+ # No need for redundant deployment here.
295
288
 
296
289
  # Set browser opening flag for monitor mode
297
290
  if monitor_mode:
claude_mpm/cli/parser.py CHANGED
@@ -306,6 +306,42 @@ def create_parser(prog_name: str = "claude-mpm", version: str = "0.0.0") -> argp
306
306
  action="store_true",
307
307
  help="List deployed agents"
308
308
  )
309
+ list_agents_parser.add_argument(
310
+ "--by-tier",
311
+ action="store_true",
312
+ help="List agents grouped by precedence tier (PROJECT > USER > SYSTEM)"
313
+ )
314
+
315
+ # View agent details
316
+ view_agent_parser = agents_subparsers.add_parser(
317
+ AgentCommands.VIEW.value,
318
+ help="View detailed information about a specific agent"
319
+ )
320
+ view_agent_parser.add_argument(
321
+ "agent_name",
322
+ help="Name of the agent to view"
323
+ )
324
+
325
+ # Fix agent frontmatter
326
+ fix_agents_parser = agents_subparsers.add_parser(
327
+ AgentCommands.FIX.value,
328
+ help="Fix agent frontmatter issues"
329
+ )
330
+ fix_agents_parser.add_argument(
331
+ "agent_name",
332
+ nargs="?",
333
+ help="Name of specific agent to fix (fix all if not specified with --all)"
334
+ )
335
+ fix_agents_parser.add_argument(
336
+ "--dry-run",
337
+ action="store_true",
338
+ help="Preview changes without applying them"
339
+ )
340
+ fix_agents_parser.add_argument(
341
+ "--all",
342
+ action="store_true",
343
+ help="Fix all agents"
344
+ )
309
345
 
310
346
  # Deploy agents
311
347
  deploy_agents_parser = agents_subparsers.add_parser(
claude_mpm/cli/utils.py CHANGED
@@ -61,7 +61,15 @@ def get_agent_versions_display() -> Optional[str]:
61
61
  """
62
62
  try:
63
63
  from ..services import AgentDeploymentService
64
- deployment_service = AgentDeploymentService()
64
+ import os
65
+ from pathlib import Path
66
+
67
+ # Determine the user's working directory from environment
68
+ user_working_dir = None
69
+ if 'CLAUDE_MPM_USER_PWD' in os.environ:
70
+ user_working_dir = Path(os.environ['CLAUDE_MPM_USER_PWD'])
71
+
72
+ deployment_service = AgentDeploymentService(working_directory=user_working_dir)
65
73
 
66
74
  # Get deployed agents
67
75
  verification = deployment_service.verify_deployment()