claude-mpm 4.6.1__py3-none-any.whl → 4.7.1__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 (111) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_ENGINEER.md +206 -48
  3. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +787 -0
  4. claude_mpm/agents/base_agent_loader.py +3 -1
  5. claude_mpm/agents/templates/engineer.json +10 -4
  6. claude_mpm/agents/templates/prompt-engineer.json +517 -87
  7. claude_mpm/cli/commands/cleanup.py +1 -1
  8. claude_mpm/cli/commands/mcp_setup_external.py +2 -2
  9. claude_mpm/cli/commands/memory.py +1 -1
  10. claude_mpm/cli/commands/mpm_init.py +5 -4
  11. claude_mpm/cli/commands/run.py +4 -4
  12. claude_mpm/cli/shared/argument_patterns.py +18 -11
  13. claude_mpm/cli/shared/base_command.py +1 -1
  14. claude_mpm/config/experimental_features.py +3 -3
  15. claude_mpm/config/socketio_config.py +1 -1
  16. claude_mpm/core/cache.py +2 -2
  17. claude_mpm/core/claude_runner.py +5 -7
  18. claude_mpm/core/container.py +10 -4
  19. claude_mpm/core/file_utils.py +10 -8
  20. claude_mpm/core/framework/formatters/context_generator.py +3 -2
  21. claude_mpm/core/framework/loaders/agent_loader.py +11 -7
  22. claude_mpm/core/injectable_service.py +11 -8
  23. claude_mpm/core/interactive_session.py +5 -4
  24. claude_mpm/core/oneshot_session.py +3 -2
  25. claude_mpm/core/pm_hook_interceptor.py +15 -9
  26. claude_mpm/core/unified_paths.py +6 -5
  27. claude_mpm/dashboard/api/simple_directory.py +16 -17
  28. claude_mpm/hooks/claude_hooks/event_handlers.py +3 -2
  29. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +2 -2
  30. claude_mpm/hooks/claude_hooks/hook_handler_original.py +2 -2
  31. claude_mpm/hooks/claude_hooks/installer.py +10 -10
  32. claude_mpm/hooks/claude_hooks/response_tracking.py +3 -2
  33. claude_mpm/hooks/claude_hooks/services/state_manager.py +3 -2
  34. claude_mpm/hooks/tool_call_interceptor.py +6 -3
  35. claude_mpm/models/agent_session.py +3 -1
  36. claude_mpm/scripts/mcp_server.py +3 -5
  37. claude_mpm/services/agents/agent_builder.py +4 -4
  38. claude_mpm/services/agents/deployment/deployment_type_detector.py +10 -14
  39. claude_mpm/services/agents/deployment/local_template_deployment.py +6 -3
  40. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +15 -11
  41. claude_mpm/services/agents/deployment/system_instructions_deployer.py +9 -6
  42. claude_mpm/services/agents/loading/agent_profile_loader.py +1 -2
  43. claude_mpm/services/agents/memory/agent_memory_manager.py +27 -27
  44. claude_mpm/services/agents/memory/content_manager.py +9 -4
  45. claude_mpm/services/claude_session_logger.py +5 -8
  46. claude_mpm/services/cli/memory_crud_service.py +1 -1
  47. claude_mpm/services/cli/memory_output_formatter.py +1 -1
  48. claude_mpm/services/cli/startup_checker.py +13 -10
  49. claude_mpm/services/cli/unified_dashboard_manager.py +10 -6
  50. claude_mpm/services/command_deployment_service.py +9 -7
  51. claude_mpm/services/core/path_resolver.py +8 -5
  52. claude_mpm/services/diagnostics/checks/agent_check.py +4 -7
  53. claude_mpm/services/diagnostics/checks/installation_check.py +19 -16
  54. claude_mpm/services/diagnostics/checks/mcp_services_check.py +30 -28
  55. claude_mpm/services/diagnostics/checks/startup_log_check.py +5 -3
  56. claude_mpm/services/events/core.py +2 -3
  57. claude_mpm/services/framework_claude_md_generator/content_validator.py +2 -2
  58. claude_mpm/services/hook_installer_service.py +2 -3
  59. claude_mpm/services/hook_service.py +5 -6
  60. claude_mpm/services/mcp_gateway/auto_configure.py +4 -5
  61. claude_mpm/services/mcp_gateway/main.py +7 -4
  62. claude_mpm/services/mcp_gateway/server/stdio_server.py +3 -4
  63. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -2
  64. claude_mpm/services/mcp_service_verifier.py +18 -17
  65. claude_mpm/services/memory/builder.py +1 -2
  66. claude_mpm/services/memory/indexed_memory.py +1 -1
  67. claude_mpm/services/memory/optimizer.py +1 -2
  68. claude_mpm/services/monitor/daemon_manager.py +3 -3
  69. claude_mpm/services/monitor/handlers/file.py +5 -4
  70. claude_mpm/services/monitor/management/lifecycle.py +1 -1
  71. claude_mpm/services/monitor/server.py +14 -12
  72. claude_mpm/services/project/architecture_analyzer.py +5 -5
  73. claude_mpm/services/project/metrics_collector.py +4 -4
  74. claude_mpm/services/project/project_organizer.py +4 -4
  75. claude_mpm/services/project/registry.py +9 -3
  76. claude_mpm/services/shared/config_service_base.py +10 -11
  77. claude_mpm/services/socketio/handlers/file.py +5 -4
  78. claude_mpm/services/socketio/handlers/git.py +7 -7
  79. claude_mpm/services/socketio/server/core.py +10 -10
  80. claude_mpm/services/subprocess_launcher_service.py +5 -10
  81. claude_mpm/services/ticket_services/formatter_service.py +1 -1
  82. claude_mpm/services/ticket_services/validation_service.py +5 -5
  83. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +5 -5
  84. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +4 -4
  85. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +4 -4
  86. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +4 -4
  87. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +4 -4
  88. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +6 -2
  89. claude_mpm/services/unified/config_strategies/unified_config_service.py +24 -13
  90. claude_mpm/services/version_control/conflict_resolution.py +6 -2
  91. claude_mpm/services/version_control/git_operations.py +1 -1
  92. claude_mpm/services/version_control/version_parser.py +1 -1
  93. claude_mpm/storage/state_storage.py +3 -3
  94. claude_mpm/tools/__main__.py +1 -1
  95. claude_mpm/tools/code_tree_analyzer.py +17 -14
  96. claude_mpm/tools/socketio_debug.py +7 -7
  97. claude_mpm/utils/common.py +6 -2
  98. claude_mpm/utils/config_manager.py +9 -3
  99. claude_mpm/utils/database_connector.py +4 -4
  100. claude_mpm/utils/dependency_strategies.py +1 -1
  101. claude_mpm/utils/environment_context.py +3 -2
  102. claude_mpm/utils/file_utils.py +1 -2
  103. claude_mpm/utils/path_operations.py +3 -1
  104. claude_mpm/utils/robust_installer.py +3 -4
  105. claude_mpm/validation/frontmatter_validator.py +4 -4
  106. {claude_mpm-4.6.1.dist-info → claude_mpm-4.7.1.dist-info}/METADATA +1 -1
  107. {claude_mpm-4.6.1.dist-info → claude_mpm-4.7.1.dist-info}/RECORD +111 -110
  108. {claude_mpm-4.6.1.dist-info → claude_mpm-4.7.1.dist-info}/WHEEL +0 -0
  109. {claude_mpm-4.6.1.dist-info → claude_mpm-4.7.1.dist-info}/entry_points.txt +0 -0
  110. {claude_mpm-4.6.1.dist-info → claude_mpm-4.7.1.dist-info}/licenses/LICENSE +0 -0
  111. {claude_mpm-4.6.1.dist-info → claude_mpm-4.7.1.dist-info}/top_level.txt +0 -0
@@ -538,7 +538,7 @@ def _cleanup_memory_original(args):
538
538
  )
539
539
  print(f"💾 Saved: {format_size(original_size - new_size)}")
540
540
  else:
541
- print("ℹ️ No conversations were old enough to clean up")
541
+ print("[INFO]️ No conversations were old enough to clean up")
542
542
  print("💡 Try using --days with a smaller value to clean more aggressively")
543
543
 
544
544
  except Exception as e:
@@ -527,11 +527,11 @@ class MCPExternalServicesSetup:
527
527
  module_name = service_info.get("module_name", service_name.replace("-", "_"))
528
528
  if not self._check_python_package(module_name):
529
529
  print(f" ⚠️ Python package {service_info['package_name']} not installed")
530
- print(f" ℹ️ Installing {service_info['package_name']}...")
530
+ print(f" [INFO]️ Installing {service_info['package_name']}...")
531
531
  if not self._install_python_package(service_info["package_name"]):
532
532
  print(f" ❌ Failed to install {service_info['package_name']}")
533
533
  print(
534
- f" ℹ️ Install manually with: pip install {service_info['package_name']}"
534
+ f" [INFO]️ Install manually with: pip install {service_info['package_name']}"
535
535
  )
536
536
  return False
537
537
 
@@ -39,7 +39,7 @@ class MemoryManagementCommand(MemoryCommand):
39
39
  config_loader = ConfigLoader()
40
40
  config = config_loader.load_main_config()
41
41
  # Use CLAUDE_MPM_USER_PWD if available, otherwise use current working directory
42
- user_pwd = os.environ.get("CLAUDE_MPM_USER_PWD", os.getcwd())
42
+ user_pwd = os.environ.get("CLAUDE_MPM_USER_PWD", Path.cwd())
43
43
  current_dir = Path(user_pwd)
44
44
  self._memory_manager = AgentMemoryManager(config, current_dir)
45
45
  return self._memory_manager
@@ -436,7 +436,7 @@ The final CLAUDE.md should be a comprehensive, well-organized guide that any AI
436
436
  console.print(f" • {pattern}")
437
437
 
438
438
  if analysis.get("custom_sections"):
439
- console.print("\n[blue]ℹ️ Custom sections found:[/blue]")
439
+ console.print("\n[blue][INFO]️ Custom sections found:[/blue]")
440
440
  for section in analysis["custom_sections"][:5]:
441
441
  console.print(f" • {section}")
442
442
 
@@ -912,10 +912,9 @@ preserving valuable project-specific information while refreshing standard secti
912
912
  )
913
913
  finally:
914
914
  # Clean up temporary file
915
- import os
916
915
 
917
916
  with contextlib.suppress(Exception):
918
- os.unlink(prompt_file)
917
+ Path(prompt_file).unlink()
919
918
 
920
919
  # Display output if verbose
921
920
  if verbose and result.stdout:
@@ -965,7 +964,9 @@ preserving valuable project-specific information while refreshing standard secti
965
964
  if "libmamba" in error_msg:
966
965
  lines = error_msg.split("\n")
967
966
  error_lines = [
968
- l for l in lines if not l.startswith("warning") and l.strip()
967
+ line
968
+ for line in lines
969
+ if not line.startswith("warning") and line.strip()
969
970
  ]
970
971
  error_msg = "\n".join(error_lines) if error_lines else error_msg
971
972
 
@@ -326,7 +326,7 @@ class RunCommand(BaseCommand):
326
326
  self.logger.warning(f"Session {resume_session_id} not found")
327
327
  else:
328
328
  self.logger.info("No recent interactive sessions found")
329
- print("ℹ️ No recent interactive sessions found to resume")
329
+ print("[INFO]️ No recent interactive sessions found to resume")
330
330
  else:
331
331
  # Resume specific session by ID
332
332
  resume_session_id = args.mpm_resume
@@ -654,7 +654,7 @@ def _handle_reload_agents(logger):
654
654
  print(f"🔄 Cleaned {removed_count} claude-mpm system agents")
655
655
  else:
656
656
  logger.info("No system agents found to clean")
657
- print("ℹ️ No system agents found - already clean")
657
+ print("[INFO]️ No system agents found - already clean")
658
658
 
659
659
  if preserved_agents:
660
660
  logger.info(f"Preserved {len(preserved_agents)} user-created agents")
@@ -774,7 +774,7 @@ def run_session_legacy(args):
774
774
  logger.warning(f"Session {resume_session_id} not found")
775
775
  else:
776
776
  logger.info("No recent interactive sessions found")
777
- print("ℹ️ No recent interactive sessions found to resume")
777
+ print("[INFO]️ No recent interactive sessions found to resume")
778
778
  else:
779
779
  # Resume specific session by ID
780
780
  resume_session_id = args.mpm_resume
@@ -947,7 +947,7 @@ def run_session_legacy(args):
947
947
  raw_claude_args = ["--resume", *raw_claude_args]
948
948
  logger.info("✅ Added --resume to claude_args")
949
949
  else:
950
- logger.info("ℹ️ --resume already in claude_args")
950
+ logger.info("[INFO]️ --resume already in claude_args")
951
951
 
952
952
  # Filter out claude-mpm specific flags before passing to Claude CLI
953
953
  logger.debug(f"Pre-filter claude_args: {raw_claude_args}")
@@ -20,65 +20,72 @@ class CommonArguments:
20
20
  "help": "Enable verbose output",
21
21
  }
22
22
 
23
- QUIET = {
23
+ QUIET: ClassVar[Dict[str, Any]] = {
24
24
  "flags": ["-q", "--quiet"],
25
25
  "action": "store_true",
26
26
  "help": "Suppress non-error output",
27
27
  }
28
28
 
29
- DEBUG = {
29
+ DEBUG: ClassVar[Dict[str, Any]] = {
30
30
  "flags": ["--debug"],
31
31
  "action": "store_true",
32
32
  "help": "Enable debug logging",
33
33
  }
34
34
 
35
35
  # Configuration arguments
36
- CONFIG_FILE = {
36
+ CONFIG_FILE: ClassVar[Dict[str, Any]] = {
37
37
  "flags": ["-c", "--config"],
38
38
  "type": Path,
39
39
  "help": "Path to configuration file",
40
40
  }
41
41
 
42
- CONFIG_DIR = {
42
+ CONFIG_DIR: ClassVar[Dict[str, Any]] = {
43
43
  "flags": ["--config-dir"],
44
44
  "type": Path,
45
45
  "help": "Configuration directory path",
46
46
  }
47
47
 
48
48
  # Output arguments
49
- OUTPUT_FORMAT = {
49
+ OUTPUT_FORMAT: ClassVar[Dict[str, Any]] = {
50
50
  "flags": ["-f", "--format"],
51
51
  "choices": ["json", "yaml", "table", "text"],
52
52
  "default": "text",
53
53
  "help": "Output format",
54
54
  }
55
55
 
56
- OUTPUT_FILE = {
56
+ OUTPUT_FILE: ClassVar[Dict[str, Any]] = {
57
57
  "flags": ["-o", "--output"],
58
58
  "type": Path,
59
59
  "help": "Output file path",
60
60
  }
61
61
 
62
62
  # Common flags
63
- FORCE = {
63
+ FORCE: ClassVar[Dict[str, Any]] = {
64
64
  "flags": ["--force"],
65
65
  "action": "store_true",
66
66
  "help": "Force operation without confirmation",
67
67
  }
68
68
 
69
- DRY_RUN = {
69
+ DRY_RUN: ClassVar[Dict[str, Any]] = {
70
70
  "flags": ["--dry-run"],
71
71
  "action": "store_true",
72
72
  "help": "Show what would be done without executing",
73
73
  }
74
74
 
75
75
  # Agent-related arguments
76
- AGENT_NAME = {"flags": ["--agent"], "help": "Agent name or pattern"}
76
+ AGENT_NAME: ClassVar[Dict[str, Any]] = {
77
+ "flags": ["--agent"],
78
+ "help": "Agent name or pattern",
79
+ }
77
80
 
78
- AGENT_DIR = {"flags": ["--agent-dir"], "type": Path, "help": "Agent directory path"}
81
+ AGENT_DIR: ClassVar[Dict[str, Any]] = {
82
+ "flags": ["--agent-dir"],
83
+ "type": Path,
84
+ "help": "Agent directory path",
85
+ }
79
86
 
80
87
  # Memory-related arguments
81
- MEMORY_DIR = {
88
+ MEMORY_DIR: ClassVar[Dict[str, Any]] = {
82
89
  "flags": ["--memory-dir"],
83
90
  "type": Path,
84
91
  "help": "Memory directory path",
@@ -73,7 +73,7 @@ class BaseCommand(ABC):
73
73
  """Get working directory (respects CLAUDE_MPM_USER_PWD)."""
74
74
  if self._working_dir is None:
75
75
  # Use CLAUDE_MPM_USER_PWD if available (when called via shell script)
76
- user_pwd = os.environ.get("CLAUDE_MPM_USER_PWD", os.getcwd())
76
+ user_pwd = os.environ.get("CLAUDE_MPM_USER_PWD", Path.cwd())
77
77
  self._working_dir = Path(user_pwd)
78
78
  return self._working_dir
79
79
 
@@ -12,7 +12,7 @@ gradual rollout of experimental features while maintaining stability in producti
12
12
 
13
13
  import json
14
14
  import os
15
- from typing import Dict, Optional
15
+ from typing import ClassVar, Dict, Optional
16
16
 
17
17
 
18
18
  class ExperimentalFeatures:
@@ -26,7 +26,7 @@ class ExperimentalFeatures:
26
26
  """
27
27
 
28
28
  # Default feature flags
29
- DEFAULTS = {
29
+ DEFAULTS: ClassVar[Dict[str, bool]] = {
30
30
  "enable_mcp_gateway": False, # MCP Gateway is experimental
31
31
  "enable_advanced_aggregation": False, # Advanced aggregation features
32
32
  "show_experimental_warnings": True, # Show warnings for experimental features
@@ -34,7 +34,7 @@ class ExperimentalFeatures:
34
34
  }
35
35
 
36
36
  # Warning messages for experimental features
37
- WARNINGS = {
37
+ WARNINGS: ClassVar[Dict[str, str]] = {
38
38
  "mcp_gateway": (
39
39
  "⚠️ EXPERIMENTAL FEATURE: MCP Gateway is in early access.\n"
40
40
  " Tool integration may be unstable. Not recommended for production use."
@@ -186,7 +186,7 @@ class ConfigManager:
186
186
  def detect_environment(self) -> str:
187
187
  """Detect the current deployment environment."""
188
188
  # Check for Docker
189
- if os.path.exists("/.dockerenv") or os.getenv("DOCKER_CONTAINER"):
189
+ if Path("/.dockerenv").exists() or os.getenv("DOCKER_CONTAINER"):
190
190
  return "docker"
191
191
 
192
192
  # Check for production indicators
claude_mpm/core/cache.py CHANGED
@@ -288,10 +288,10 @@ class FileSystemCache:
288
288
  """Read file from disk."""
289
289
  try:
290
290
  if "b" in mode:
291
- with open(file_path, mode) as f:
291
+ with Path(file_path).open(mode) as f:
292
292
  return f.read()
293
293
  else:
294
- with open(file_path, mode, encoding=encoding) as f:
294
+ with Path(file_path).open(mode, encoding=encoding) as f:
295
295
  return f.read()
296
296
  except Exception as e:
297
297
  self._logger.error(f"Failed to read file {file_path}: {e}")
@@ -462,13 +462,11 @@ class ClaudeRunner:
462
462
  if (
463
463
  "author: claude-mpm-project" in existing_content
464
464
  or "source: project" in existing_content
465
- ):
466
- # Compare modification times
467
- if target_file.stat().st_mtime >= json_file.stat().st_mtime:
468
- needs_update = False
469
- self.logger.debug(
470
- f"Project agent {agent_name} is up to date"
471
- )
465
+ ) and target_file.stat().st_mtime >= json_file.stat().st_mtime:
466
+ needs_update = False
467
+ self.logger.debug(
468
+ f"Project agent {agent_name} is up to date"
469
+ )
472
470
 
473
471
  if needs_update:
474
472
  # Build the agent markdown using the pre-initialized service and base agent data
@@ -538,10 +538,16 @@ class DIContainer(IServiceContainer):
538
538
 
539
539
  elif registration.lifetime == ServiceLifetime.SCOPED:
540
540
  # Check current scope
541
- if self._current_scope:
542
- instance = self._current_scope.get_scoped_instance(service_type)
543
- if instance is not None:
544
- return instance
541
+ if (
542
+ self._current_scope
543
+ and (
544
+ instance := self._current_scope.get_scoped_instance(
545
+ service_type
546
+ )
547
+ )
548
+ is not None
549
+ ):
550
+ return instance
545
551
 
546
552
  # Mark as resolving
547
553
  self._resolving.add(service_type)
@@ -173,10 +173,10 @@ def safe_read(
173
173
 
174
174
  try:
175
175
  if "b" in mode:
176
- with open(filepath, mode) as f:
176
+ with Path(filepath).open(mode) as f:
177
177
  return f.read()
178
178
  else:
179
- with open(filepath, mode, encoding=encoding) as f:
179
+ with Path(filepath).open(mode, encoding=encoding) as f:
180
180
  return f.read()
181
181
  except (OSError, UnicodeDecodeError) as e:
182
182
  logger.error(f"Error reading file {filepath}: {e}")
@@ -206,7 +206,9 @@ def safe_read_lines(
206
206
  return []
207
207
 
208
208
  try:
209
- with open(filepath, encoding=encoding) as f:
209
+ with Path(filepath).open(
210
+ encoding=encoding,
211
+ ) as f:
210
212
  lines = []
211
213
  for i, line in enumerate(f):
212
214
  if max_lines is not None and i >= max_lines:
@@ -320,10 +322,10 @@ def safe_write(
320
322
 
321
323
  try:
322
324
  if "b" in mode:
323
- with open(filepath, mode) as f:
325
+ with Path(filepath).open(mode) as f:
324
326
  f.write(content)
325
327
  else:
326
- with open(filepath, mode, encoding=encoding) as f:
328
+ with Path(filepath).open(mode, encoding=encoding) as f:
327
329
  f.write(content)
328
330
  return True
329
331
  except OSError as e:
@@ -368,14 +370,14 @@ def atomic_write(
368
370
  f.write(content)
369
371
 
370
372
  # Atomic rename
371
- os.replace(temp_path, filepath)
373
+ Path(temp_path).replace(filepath)
372
374
  return True
373
375
 
374
376
  except OSError as e:
375
377
  logger.error(f"Error in atomic write to {filepath}: {e}")
376
378
  # Clean up temporary file
377
379
  with suppress(Exception):
378
- os.unlink(temp_path)
380
+ Path(temp_path).unlink()
379
381
  return False
380
382
 
381
383
 
@@ -634,7 +636,7 @@ def file_lock(filepath: Union[str, Path], timeout: float = 5.0):
634
636
 
635
637
  while True:
636
638
  try:
637
- lock_handle = open(lock_file, "w")
639
+ lock_handle = Path(lock_file).open("w")
638
640
  fcntl.flock(lock_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
639
641
  break
640
642
  except OSError as e:
@@ -6,6 +6,7 @@ import os
6
6
  import platform
7
7
  import time as time_module
8
8
  from datetime import datetime, timezone
9
+ from pathlib import Path
9
10
 
10
11
  from claude_mpm.core.logging_utils import get_logger
11
12
 
@@ -117,7 +118,7 @@ class ContextGenerator:
117
118
 
118
119
  # Add home directory if available
119
120
  try:
120
- home_dir = os.path.expanduser("~")
121
+ home_dir = Path("~").expanduser()
121
122
  if home_dir and home_dir != "~":
122
123
  context_lines.append(f"**Home Directory**: {home_dir}\n")
123
124
  except Exception:
@@ -168,7 +169,7 @@ class ContextGenerator:
168
169
  """
169
170
  try:
170
171
  # Add current working directory
171
- cwd = os.getcwd()
172
+ cwd = Path.cwd()
172
173
  if cwd:
173
174
  context_lines.append(f"**Working Directory**: {cwd}\n")
174
175
  except Exception:
@@ -100,13 +100,17 @@ class AgentLoader:
100
100
  agents[agent_name] = agent_content
101
101
 
102
102
  # If we used templates dir, also check main dir for base_agent.md
103
- if agents_dir == templates_dir and main_dir and main_dir.exists():
104
- if "base_agent" not in agents:
105
- base_agent_file = main_dir / "base_agent.md"
106
- if base_agent_file.exists():
107
- agent_name, agent_content = self.load_single_agent(base_agent_file)
108
- if agent_name and agent_content:
109
- agents[agent_name] = agent_content
103
+ if (
104
+ agents_dir == templates_dir
105
+ and main_dir
106
+ and main_dir.exists()
107
+ and "base_agent" not in agents
108
+ ):
109
+ base_agent_file = main_dir / "base_agent.md"
110
+ if base_agent_file.exists():
111
+ agent_name, agent_content = self.load_single_agent(base_agent_file)
112
+ if agent_name and agent_content:
113
+ agents[agent_name] = agent_content
110
114
 
111
115
  return agents
112
116
 
@@ -182,14 +182,17 @@ class InjectableService(BaseService, ABC):
182
182
  continue
183
183
 
184
184
  attr = getattr(self, attr_name, None)
185
- if attr and hasattr(attr, "start") and hasattr(attr, "running"):
186
- # It's a service that needs to be started
187
- if not attr.running:
188
- try:
189
- await attr.start()
190
- logger.debug(f"Started dependency {attr_name}")
191
- except Exception as e:
192
- logger.warning(f"Failed to start dependency {attr_name}: {e}")
185
+ if (
186
+ attr
187
+ and hasattr(attr, "start")
188
+ and hasattr(attr, "running")
189
+ and not attr.running
190
+ ):
191
+ try:
192
+ await attr.start()
193
+ logger.debug(f"Started dependency {attr_name}")
194
+ except Exception as e:
195
+ logger.warning(f"Failed to start dependency {attr_name}: {e}")
193
196
 
194
197
  def __repr__(self) -> str:
195
198
  """Enhanced string representation showing dependencies."""
@@ -8,6 +8,7 @@ import contextlib
8
8
  import os
9
9
  import subprocess
10
10
  import uuid
11
+ from pathlib import Path
11
12
  from typing import Any, Dict, Optional, Tuple
12
13
 
13
14
  from claude_mpm.core.logger import get_logger
@@ -35,7 +36,7 @@ class InteractiveSession:
35
36
  self.runner = runner
36
37
  self.logger = get_logger("interactive_session")
37
38
  self.session_id = None
38
- self.original_cwd = os.getcwd()
39
+ self.original_cwd = Path.cwd()
39
40
 
40
41
  # Initialize response tracking for interactive sessions
41
42
  # WHY: Interactive sessions need response logging just like oneshot sessions.
@@ -225,7 +226,7 @@ class InteractiveSession:
225
226
  """
226
227
  try:
227
228
  # Restore original directory
228
- if self.original_cwd and os.path.exists(self.original_cwd):
229
+ if self.original_cwd and Path(self.original_cwd).exists():
229
230
  with contextlib.suppress(OSError):
230
231
  os.chdir(self.original_cwd)
231
232
 
@@ -278,7 +279,7 @@ class InteractiveSession:
278
279
  self.runner.websocket_server.session_started(
279
280
  session_id=self.session_id,
280
281
  launch_method=self.runner.launch_method,
281
- working_dir=os.getcwd(),
282
+ working_dir=Path.cwd(),
282
283
  )
283
284
  return True, None
284
285
 
@@ -392,7 +393,7 @@ class InteractiveSession:
392
393
  self.logger.info("✅ VERIFIED: --resume flag IS included in final command")
393
394
  self.logger.debug(f"--resume position in command: {cmd.index('--resume')}")
394
395
  else:
395
- self.logger.debug("ℹ️ --resume flag NOT included in final command")
396
+ self.logger.debug("[INFO]️ --resume flag NOT included in final command")
396
397
 
397
398
  return cmd
398
399
 
@@ -9,6 +9,7 @@ import os
9
9
  import subprocess
10
10
  import time
11
11
  import uuid
12
+ from pathlib import Path
12
13
  from typing import Any, Dict, Optional, Tuple
13
14
 
14
15
  from claude_mpm.core.logger import get_logger
@@ -97,7 +98,7 @@ class OneshotSession:
97
98
  infrastructure["env"]["CLAUDE_WORKSPACE"] = user_pwd
98
99
 
99
100
  try:
100
- self.original_cwd = os.getcwd()
101
+ self.original_cwd = Path.cwd()
101
102
  os.chdir(user_pwd)
102
103
  infrastructure["working_dir_changed"] = True
103
104
  self.logger.info(f"Changed working directory to: {user_pwd}")
@@ -242,7 +243,7 @@ class OneshotSession:
242
243
  self.runner.websocket_server.session_started(
243
244
  session_id=self.session_id,
244
245
  launch_method="oneshot",
245
- working_dir=os.getcwd(),
246
+ working_dir=Path.cwd(),
246
247
  )
247
248
  except (ImportError, ConnectionError, Exception) as e:
248
249
  self.logger.warning(f"Socket.IO connection failed: {e}")
@@ -139,10 +139,13 @@ class PMHookInterceptor:
139
139
 
140
140
  # Look for todos in positional args
141
141
  for arg in args:
142
- if isinstance(arg, list) and arg and isinstance(arg[0], dict):
143
- # Check if this looks like a todos list
144
- if "content" in arg[0] or "id" in arg[0]:
145
- return arg
142
+ if (
143
+ isinstance(arg, list)
144
+ and arg
145
+ and isinstance(arg[0], dict)
146
+ and ("content" in arg[0] or "id" in arg[0])
147
+ ):
148
+ return arg
146
149
 
147
150
  return []
148
151
 
@@ -168,11 +171,14 @@ class PMHookInterceptor:
168
171
  # Update positional args if todos was passed positionally
169
172
  args_list = list(args)
170
173
  for i, arg in enumerate(args_list):
171
- if isinstance(arg, list) and arg and isinstance(arg[0], dict):
172
- # Check if this looks like a todos list
173
- if "content" in arg[0] or "id" in arg[0]:
174
- args_list[i] = modified_todos
175
- return tuple(args_list), kwargs
174
+ if (
175
+ isinstance(arg, list)
176
+ and arg
177
+ and isinstance(arg[0], dict)
178
+ and ("content" in arg[0] or "id" in arg[0])
179
+ ):
180
+ args_list[i] = modified_todos
181
+ return tuple(args_list), kwargs
176
182
 
177
183
  # If we can't find where todos was passed, add as keyword argument
178
184
  kwargs = kwargs.copy()
@@ -83,11 +83,12 @@ class PathContext:
83
83
  # Check if we're in a src/ directory structure with pyproject.toml
84
84
  current = module_path
85
85
  for _ in range(5): # Check up to 5 levels up
86
- if (current / "pyproject.toml").exists():
87
- # Found pyproject.toml, check if this looks like a development setup
88
- if (current / "src" / "claude_mpm").exists():
89
- logger.debug(f"Found development installation at {current}")
90
- return True
86
+ if (current / "pyproject.toml").exists() and (
87
+ current / "src" / "claude_mpm"
88
+ ).exists():
89
+ # Found pyproject.toml with development setup
90
+ logger.debug(f"Found development installation at {current}")
91
+ return True
91
92
  if current == current.parent:
92
93
  break
93
94
  current = current.parent