claude-mpm 4.0.29__py3-none-any.whl → 4.0.30__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 (64) hide show
  1. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +48 -3
  2. claude_mpm/agents/BASE_PM.md +20 -15
  3. claude_mpm/agents/INSTRUCTIONS.md +12 -2
  4. claude_mpm/agents/templates/documentation.json +16 -3
  5. claude_mpm/agents/templates/engineer.json +19 -5
  6. claude_mpm/agents/templates/ops.json +19 -5
  7. claude_mpm/agents/templates/qa.json +16 -3
  8. claude_mpm/agents/templates/refactoring_engineer.json +25 -7
  9. claude_mpm/agents/templates/research.json +19 -5
  10. claude_mpm/cli/__init__.py +2 -0
  11. claude_mpm/cli/commands/__init__.py +2 -0
  12. claude_mpm/cli/commands/agent_manager.py +10 -6
  13. claude_mpm/cli/commands/agents.py +2 -1
  14. claude_mpm/cli/commands/cleanup.py +1 -1
  15. claude_mpm/cli/commands/doctor.py +209 -0
  16. claude_mpm/cli/commands/mcp.py +3 -3
  17. claude_mpm/cli/commands/mcp_install_commands.py +12 -30
  18. claude_mpm/cli/commands/mcp_server_commands.py +9 -9
  19. claude_mpm/cli/commands/run.py +31 -2
  20. claude_mpm/cli/commands/run_config_checker.py +1 -1
  21. claude_mpm/cli/parsers/agent_manager_parser.py +3 -3
  22. claude_mpm/cli/parsers/base_parser.py +5 -1
  23. claude_mpm/cli/parsers/mcp_parser.py +1 -1
  24. claude_mpm/cli/parsers/run_parser.py +1 -1
  25. claude_mpm/cli/startup_logging.py +463 -0
  26. claude_mpm/constants.py +1 -0
  27. claude_mpm/core/claude_runner.py +78 -0
  28. claude_mpm/core/framework_loader.py +45 -11
  29. claude_mpm/core/interactive_session.py +82 -3
  30. claude_mpm/core/output_style_manager.py +6 -6
  31. claude_mpm/core/unified_paths.py +128 -0
  32. claude_mpm/scripts/mcp_server.py +2 -2
  33. claude_mpm/services/agents/deployment/agent_validator.py +1 -0
  34. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +69 -1
  35. claude_mpm/services/diagnostics/__init__.py +18 -0
  36. claude_mpm/services/diagnostics/checks/__init__.py +30 -0
  37. claude_mpm/services/diagnostics/checks/agent_check.py +319 -0
  38. claude_mpm/services/diagnostics/checks/base_check.py +64 -0
  39. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +283 -0
  40. claude_mpm/services/diagnostics/checks/common_issues_check.py +354 -0
  41. claude_mpm/services/diagnostics/checks/configuration_check.py +300 -0
  42. claude_mpm/services/diagnostics/checks/filesystem_check.py +233 -0
  43. claude_mpm/services/diagnostics/checks/installation_check.py +255 -0
  44. claude_mpm/services/diagnostics/checks/mcp_check.py +315 -0
  45. claude_mpm/services/diagnostics/checks/monitor_check.py +282 -0
  46. claude_mpm/services/diagnostics/checks/startup_log_check.py +322 -0
  47. claude_mpm/services/diagnostics/diagnostic_runner.py +247 -0
  48. claude_mpm/services/diagnostics/doctor_reporter.py +283 -0
  49. claude_mpm/services/diagnostics/models.py +120 -0
  50. claude_mpm/services/mcp_gateway/core/interfaces.py +1 -1
  51. claude_mpm/services/mcp_gateway/main.py +1 -1
  52. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +3 -3
  53. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  54. claude_mpm/services/mcp_gateway/server/stdio_server.py +3 -3
  55. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +2 -2
  56. claude_mpm/services/socketio/handlers/registry.py +39 -7
  57. claude_mpm/services/socketio/server/core.py +72 -22
  58. claude_mpm/validation/frontmatter_validator.py +1 -1
  59. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/METADATA +4 -1
  60. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/RECORD +64 -47
  61. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/WHEEL +0 -0
  62. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/entry_points.txt +0 -0
  63. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/licenses/LICENSE +0 -0
  64. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/top_level.txt +0 -0
@@ -206,7 +206,7 @@ class InteractiveSession:
206
206
  return self._attempt_fallback_launch(environment)
207
207
 
208
208
  def process_interactive_command(self, prompt: str) -> Optional[bool]:
209
- """Process special interactive commands like /agents.
209
+ """Process special interactive commands like /agents and /mpm-doctor.
210
210
 
211
211
  Args:
212
212
  prompt: User input command
@@ -214,9 +214,19 @@ class InteractiveSession:
214
214
  Returns:
215
215
  Optional[bool]: True if handled, False if error, None if not a special command
216
216
  """
217
+ # Parse command and arguments
218
+ parts = prompt.strip().split()
219
+ if not parts:
220
+ return None
221
+
222
+ command = parts[0]
223
+ args = parts[1:]
224
+
217
225
  # Check for special commands
218
- if prompt.strip() == "/agents":
226
+ if command == "/agents":
219
227
  return self._show_available_agents()
228
+ elif command == "/mpm-doctor":
229
+ return self._run_doctor_diagnostics(args)
220
230
 
221
231
  # Not a special command
222
232
  return None
@@ -310,7 +320,13 @@ class InteractiveSession:
310
320
  print(f"\033[32m│\033[0m {output_style_info:<49}\033[32m│\033[0m")
311
321
  print("\033[32m│ │\033[0m")
312
322
  print(
313
- "\033[32m│\033[0m Type '/agents' to see available agents \033[32m│\033[0m"
323
+ "\033[32m│\033[0m Commands: \033[32m│\033[0m"
324
+ )
325
+ print(
326
+ "\033[32m│\033[0m /agents - Show available agents \033[32m│\033[0m"
327
+ )
328
+ print(
329
+ "\033[32m│\033[0m /mpm-doctor - Run diagnostic checks \033[32m│\033[0m"
314
330
  )
315
331
  print("\033[32m╰───────────────────────────────────────────────────╯\033[0m")
316
332
  print("") # Add blank line after box
@@ -552,3 +568,66 @@ class InteractiveSession:
552
568
  except Exception as e:
553
569
  print(f"Error getting agent versions: {e}")
554
570
  return False
571
+
572
+ def _run_doctor_diagnostics(self, args: list) -> bool:
573
+ """Run doctor diagnostics from interactive mode.
574
+
575
+ Args:
576
+ args: Command arguments (e.g., ['--verbose'])
577
+
578
+ Returns:
579
+ bool: True if successful, False otherwise
580
+ """
581
+ try:
582
+ from claude_mpm.services.diagnostics import DiagnosticRunner, DoctorReporter
583
+
584
+ # Parse arguments
585
+ verbose = "--verbose" in args or "-v" in args
586
+ no_color = "--no-color" in args
587
+
588
+ # Print header
589
+ print("\n" + "="*60)
590
+ print("Claude MPM Doctor Report")
591
+ print("="*60)
592
+
593
+ # Create diagnostic runner
594
+ runner = DiagnosticRunner(verbose=verbose)
595
+
596
+ # Run diagnostics
597
+ try:
598
+ summary = runner.run_diagnostics()
599
+ except KeyboardInterrupt:
600
+ print("\nDiagnostics interrupted by user")
601
+ return False
602
+ except Exception as e:
603
+ print(f"\n❌ Diagnostic failed: {str(e)}")
604
+ if verbose:
605
+ import traceback
606
+ traceback.print_exc()
607
+ return False
608
+
609
+ # Create reporter
610
+ reporter = DoctorReporter(
611
+ use_color=not no_color,
612
+ verbose=verbose
613
+ )
614
+
615
+ # Display results in terminal format
616
+ reporter.report(summary, format="terminal")
617
+
618
+ # Return based on status
619
+ if summary.error_count > 0:
620
+ return False
621
+
622
+ return True
623
+
624
+ except ImportError as e:
625
+ print(f"Error: Diagnostics module not available: {e}")
626
+ print("Please ensure claude-mpm is properly installed")
627
+ return False
628
+ except Exception as e:
629
+ print(f"Error running diagnostics: {e}")
630
+ if "--verbose" in args or "-v" in args:
631
+ import traceback
632
+ traceback.print_exc()
633
+ return False
@@ -3,7 +3,7 @@
3
3
  This module handles:
4
4
  1. Claude version detection
5
5
  2. Output style extraction from framework instructions
6
- 3. One-time deployment to Claude Desktop >= 1.0.83 at startup
6
+ 3. One-time deployment to Claude Code >= 1.0.83 at startup
7
7
  4. Fallback injection for older versions
8
8
 
9
9
  The output style is set once at startup and not monitored or enforced after that.
@@ -41,7 +41,7 @@ class OutputStyleManager:
41
41
 
42
42
  def _detect_claude_version(self) -> Optional[str]:
43
43
  """
44
- Detect Claude Desktop version by running 'claude --version'.
44
+ Detect Claude Code version by running 'claude --version'.
45
45
 
46
46
  Returns:
47
47
  Version string (e.g., "1.0.82") or None if Claude not found
@@ -73,7 +73,7 @@ class OutputStyleManager:
73
73
  return None
74
74
 
75
75
  except FileNotFoundError:
76
- self.logger.info("Claude Desktop not found in PATH")
76
+ self.logger.info("Claude Code not found in PATH")
77
77
  return None
78
78
  except subprocess.TimeoutExpired:
79
79
  self.logger.warning("Claude version check timed out")
@@ -116,7 +116,7 @@ class OutputStyleManager:
116
116
 
117
117
  def supports_output_styles(self) -> bool:
118
118
  """
119
- Check if Claude Desktop supports output styles (>= 1.0.83).
119
+ Check if Claude Code supports output styles (>= 1.0.83).
120
120
 
121
121
  Returns:
122
122
  True if Claude version >= 1.0.83, False otherwise
@@ -324,7 +324,7 @@ class OutputStyleManager:
324
324
 
325
325
  def deploy_output_style(self, content: str) -> bool:
326
326
  """
327
- Deploy output style to Claude Desktop if version >= 1.0.83.
327
+ Deploy output style to Claude Code if version >= 1.0.83.
328
328
  Deploys the style file and activates it once.
329
329
 
330
330
  Args:
@@ -356,7 +356,7 @@ class OutputStyleManager:
356
356
 
357
357
  def _activate_output_style(self) -> bool:
358
358
  """
359
- Update Claude Desktop settings to activate the claude-mpm output style.
359
+ Update Claude Code settings to activate the claude-mpm output style.
360
360
  Sets activeOutputStyle to "claude-mpm" once at startup.
361
361
 
362
362
  Returns:
@@ -641,6 +641,128 @@ class UnifiedPathManager:
641
641
  if src_dir.exists() and str(src_dir) not in sys.path:
642
642
  sys.path.insert(0, str(src_dir))
643
643
 
644
+ def get_executable_path(self) -> Optional[Path]:
645
+ """Get the path to the claude-mpm executable for the current deployment context.
646
+
647
+ This method provides deployment-context-aware executable path detection,
648
+ particularly useful for MCP server configuration.
649
+
650
+ Returns:
651
+ Path to executable or None if not found
652
+ """
653
+ import shutil
654
+
655
+ # Try standard which first
656
+ which_result = shutil.which("claude-mpm")
657
+ if which_result:
658
+ return Path(which_result)
659
+
660
+ # Enhanced detection based on deployment context
661
+ if self._deployment_context == DeploymentContext.PIPX_INSTALL:
662
+ return self._find_pipx_executable()
663
+ elif self._deployment_context in (DeploymentContext.DEVELOPMENT, DeploymentContext.EDITABLE_INSTALL):
664
+ return self._find_development_executable()
665
+ elif self._deployment_context == DeploymentContext.PIP_INSTALL:
666
+ return self._find_pip_executable()
667
+
668
+ return None
669
+
670
+ def _find_pipx_executable(self) -> Optional[Path]:
671
+ """Find claude-mpm executable in pipx installation."""
672
+ try:
673
+ import claude_mpm
674
+ module_path = Path(claude_mpm.__file__).parent
675
+
676
+ if "pipx" not in str(module_path):
677
+ return None
678
+
679
+ # Common pipx executable locations
680
+ home = Path.home()
681
+ pipx_paths = [
682
+ home / ".local" / "bin" / "claude-mpm",
683
+ home / ".local" / "share" / "pipx" / "venvs" / "claude-mpm" / "bin" / "claude-mpm",
684
+ ]
685
+
686
+ # Windows paths
687
+ if sys.platform == "win32":
688
+ pipx_paths.extend([
689
+ home / "AppData" / "Local" / "pipx" / "bin" / "claude-mpm.exe",
690
+ home / ".local" / "bin" / "claude-mpm.exe",
691
+ ])
692
+
693
+ for path in pipx_paths:
694
+ if path.exists():
695
+ logger.debug(f"Found pipx executable: {path}")
696
+ return path
697
+
698
+ # Try to derive from module path
699
+ # Navigate up from module to find venv, then to bin
700
+ venv_path = module_path
701
+ for _ in range(5): # Prevent infinite loops
702
+ if venv_path.name == "claude-mpm" and (venv_path / "pyvenv.cfg").exists():
703
+ # Found the venv directory
704
+ bin_dir = venv_path / ("Scripts" if sys.platform == "win32" else "bin")
705
+ exe_name = "claude-mpm.exe" if sys.platform == "win32" else "claude-mpm"
706
+ exe_path = bin_dir / exe_name
707
+
708
+ if exe_path.exists():
709
+ logger.debug(f"Found pipx executable via module path: {exe_path}")
710
+ return exe_path
711
+ break
712
+
713
+ if venv_path == venv_path.parent:
714
+ break
715
+ venv_path = venv_path.parent
716
+
717
+ except Exception as e:
718
+ logger.debug(f"Error finding pipx executable: {e}")
719
+
720
+ return None
721
+
722
+ def _find_development_executable(self) -> Optional[Path]:
723
+ """Find claude-mpm executable in development installation."""
724
+ # For development, prefer the script in the project
725
+ scripts_dir = self.get_scripts_dir()
726
+ dev_executable = scripts_dir / "claude-mpm"
727
+
728
+ if dev_executable.exists():
729
+ return dev_executable
730
+
731
+ # Check if we're in a development venv
732
+ if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
733
+ venv_bin = Path(sys.prefix) / ("Scripts" if sys.platform == "win32" else "bin")
734
+ venv_executable = venv_bin / "claude-mpm"
735
+ if venv_executable.exists():
736
+ return venv_executable
737
+
738
+ return None
739
+
740
+ def _find_pip_executable(self) -> Optional[Path]:
741
+ """Find claude-mpm executable in pip installation."""
742
+ # For pip installs, check the current Python environment
743
+ if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
744
+ # In a virtual environment
745
+ venv_bin = Path(sys.prefix) / ("Scripts" if sys.platform == "win32" else "bin")
746
+ venv_executable = venv_bin / "claude-mpm"
747
+ if venv_executable.exists():
748
+ return venv_executable
749
+
750
+ # Check system-wide installation
751
+ try:
752
+ import site
753
+ for site_dir in site.getsitepackages():
754
+ # Look for installed scripts
755
+ site_path = Path(site_dir)
756
+ scripts_dir = site_path.parent / ("Scripts" if sys.platform == "win32" else "bin")
757
+ if scripts_dir.exists():
758
+ exe_path = scripts_dir / "claude-mpm"
759
+ if exe_path.exists():
760
+ return exe_path
761
+ except Exception as e:
762
+ logger.debug(f"Error finding pip executable: {e}")
763
+
764
+ return None
765
+
644
766
 
645
767
  # ============================================================================
646
768
  # Singleton Instance and Convenience Functions
@@ -701,6 +823,11 @@ def get_package_resource_path(resource_path: str) -> Path:
701
823
  return get_path_manager().get_package_resource_path(resource_path)
702
824
 
703
825
 
826
+ def get_executable_path() -> Optional[Path]:
827
+ """Get the claude-mpm executable path for the current deployment context."""
828
+ return get_path_manager().get_executable_path()
829
+
830
+
704
831
  # ============================================================================
705
832
  # Export All Public Symbols
706
833
  # ============================================================================
@@ -719,4 +846,5 @@ __all__ = [
719
846
  "get_config_dir",
720
847
  "find_file_upwards",
721
848
  "get_package_resource_path",
849
+ "get_executable_path",
722
850
  ]
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
- """MCP Server launcher script for Claude Desktop.
2
+ """MCP Server launcher script for Claude Code.
3
3
 
4
- This script launches the MCP gateway server for Claude Desktop integration.
4
+ This script launches the MCP gateway server for Claude Code integration.
5
5
  It handles proper Python path setup and error reporting to stderr.
6
6
  """
7
7
 
@@ -324,6 +324,7 @@ class AgentValidator:
324
324
  agent_info = {
325
325
  "file": agent_file.name,
326
326
  "name": agent_file.stem,
327
+ "path": str(agent_file),
327
328
  "description": "No description",
328
329
  "version": "unknown",
329
330
  }
@@ -363,6 +363,10 @@ class MultiSourceAgentDeploymentService:
363
363
  source_match = re.search(r'^source:\s*(.+)$', deployed_content, re.MULTILINE)
364
364
  if source_match:
365
365
  deployed_source = source_match.group(1).strip()
366
+
367
+ # If source is still unknown, try to infer it from deployment context
368
+ if deployed_source == "unknown":
369
+ deployed_source = self._infer_agent_source_from_context(agent_name, deployed_agents_dir)
366
370
  except Exception as e:
367
371
  self.logger.warning(f"Error reading deployed agent '{agent_name}': {e}")
368
372
  comparison_results["needs_update"].append(agent_name)
@@ -438,4 +442,68 @@ class MultiSourceAgentDeploymentService:
438
442
  f"{downgrade['template_version']}"
439
443
  )
440
444
 
441
- return comparison_results
445
+ return comparison_results
446
+
447
+ def _infer_agent_source_from_context(self, agent_name: str, deployed_agents_dir: Path) -> str:
448
+ """Infer the source of a deployed agent when source metadata is missing.
449
+
450
+ This method attempts to determine the agent source based on:
451
+ 1. Deployment context (development vs pipx)
452
+ 2. Agent naming patterns
453
+ 3. Known system agents
454
+
455
+ Args:
456
+ agent_name: Name of the agent
457
+ deployed_agents_dir: Directory where agent is deployed
458
+
459
+ Returns:
460
+ Inferred source string (system/project/user)
461
+ """
462
+ # List of known system agents that ship with claude-mpm
463
+ system_agents = {
464
+ "pm", "engineer", "qa", "research", "documentation", "ops",
465
+ "security", "web-ui", "api-qa", "version-control"
466
+ }
467
+
468
+ # If this is a known system agent, it's from system
469
+ if agent_name in system_agents:
470
+ return "system"
471
+
472
+ # Check deployment context
473
+ from ....core.unified_paths import get_path_manager
474
+ path_manager = get_path_manager()
475
+
476
+ # If deployed_agents_dir is under user home/.claude/agents, check context
477
+ user_claude_dir = Path.home() / ".claude" / "agents"
478
+ if deployed_agents_dir == user_claude_dir:
479
+ # Check if we're in development mode
480
+ try:
481
+ from ....core.unified_paths import DeploymentContext, PathContext
482
+ deployment_context = PathContext.detect_deployment_context()
483
+
484
+ if deployment_context in (DeploymentContext.DEVELOPMENT, DeploymentContext.EDITABLE_INSTALL):
485
+ # In development mode, unknown agents are likely system agents being tested
486
+ return "system"
487
+ elif deployment_context == DeploymentContext.PIPX_INSTALL:
488
+ # In pipx mode, unknown agents could be system agents
489
+ # Check if agent follows system naming patterns
490
+ if agent_name.count('-') <= 2 and len(agent_name) <= 20:
491
+ return "system"
492
+ except Exception:
493
+ pass
494
+
495
+ # Check if deployed to project-specific directory
496
+ try:
497
+ project_root = path_manager.project_root
498
+ if str(deployed_agents_dir).startswith(str(project_root)):
499
+ return "project"
500
+ except Exception:
501
+ pass
502
+
503
+ # Default inference based on naming patterns
504
+ # System agents typically have simple names
505
+ if '-' not in agent_name or agent_name.count('-') <= 1:
506
+ return "system"
507
+
508
+ # Complex names are more likely to be user/project agents
509
+ return "user"
@@ -0,0 +1,18 @@
1
+ """
2
+ Diagnostic service for claude-mpm health checks.
3
+
4
+ WHY: Provide a comprehensive diagnostic tool to help users identify and fix
5
+ common issues with their claude-mpm installation and configuration.
6
+
7
+ DESIGN DECISIONS:
8
+ - Modular check system for easy extension
9
+ - Interface-based design for consistency
10
+ - Clear status levels (ok, warning, error)
11
+ - Actionable fix suggestions
12
+ """
13
+
14
+ from .diagnostic_runner import DiagnosticRunner
15
+ from .doctor_reporter import DoctorReporter
16
+ from .models import DiagnosticResult, DiagnosticStatus
17
+
18
+ __all__ = ["DiagnosticRunner", "DoctorReporter", "DiagnosticResult", "DiagnosticStatus"]
@@ -0,0 +1,30 @@
1
+ """
2
+ Diagnostic checks for claude-mpm doctor command.
3
+
4
+ WHY: Modular checks allow for easy extension and testing of individual
5
+ diagnostic components.
6
+ """
7
+
8
+ from .agent_check import AgentCheck
9
+ from .base_check import BaseDiagnosticCheck
10
+ from .claude_desktop_check import ClaudeDesktopCheck
11
+ from .common_issues_check import CommonIssuesCheck
12
+ from .configuration_check import ConfigurationCheck
13
+ from .filesystem_check import FilesystemCheck
14
+ from .installation_check import InstallationCheck
15
+ from .mcp_check import MCPCheck
16
+ from .monitor_check import MonitorCheck
17
+ from .startup_log_check import StartupLogCheck
18
+
19
+ __all__ = [
20
+ "BaseDiagnosticCheck",
21
+ "InstallationCheck",
22
+ "ConfigurationCheck",
23
+ "ClaudeDesktopCheck",
24
+ "AgentCheck",
25
+ "MCPCheck",
26
+ "MonitorCheck",
27
+ "FilesystemCheck",
28
+ "CommonIssuesCheck",
29
+ "StartupLogCheck",
30
+ ]