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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +206 -48
- claude_mpm/agents/BASE_PROMPT_ENGINEER.md +787 -0
- claude_mpm/agents/base_agent_loader.py +3 -1
- claude_mpm/agents/templates/engineer.json +10 -4
- claude_mpm/agents/templates/prompt-engineer.json +517 -87
- claude_mpm/cli/commands/cleanup.py +1 -1
- claude_mpm/cli/commands/mcp_setup_external.py +2 -2
- claude_mpm/cli/commands/memory.py +1 -1
- claude_mpm/cli/commands/mpm_init.py +5 -4
- claude_mpm/cli/commands/run.py +4 -4
- claude_mpm/cli/shared/argument_patterns.py +18 -11
- claude_mpm/cli/shared/base_command.py +1 -1
- claude_mpm/config/experimental_features.py +3 -3
- claude_mpm/config/socketio_config.py +1 -1
- claude_mpm/core/cache.py +2 -2
- claude_mpm/core/claude_runner.py +5 -7
- claude_mpm/core/container.py +10 -4
- claude_mpm/core/file_utils.py +10 -8
- claude_mpm/core/framework/formatters/context_generator.py +3 -2
- claude_mpm/core/framework/loaders/agent_loader.py +11 -7
- claude_mpm/core/injectable_service.py +11 -8
- claude_mpm/core/interactive_session.py +5 -4
- claude_mpm/core/oneshot_session.py +3 -2
- claude_mpm/core/pm_hook_interceptor.py +15 -9
- claude_mpm/core/unified_paths.py +6 -5
- claude_mpm/dashboard/api/simple_directory.py +16 -17
- claude_mpm/hooks/claude_hooks/event_handlers.py +3 -2
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +2 -2
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +2 -2
- claude_mpm/hooks/claude_hooks/installer.py +10 -10
- claude_mpm/hooks/claude_hooks/response_tracking.py +3 -2
- claude_mpm/hooks/claude_hooks/services/state_manager.py +3 -2
- claude_mpm/hooks/tool_call_interceptor.py +6 -3
- claude_mpm/models/agent_session.py +3 -1
- claude_mpm/scripts/mcp_server.py +3 -5
- claude_mpm/services/agents/agent_builder.py +4 -4
- claude_mpm/services/agents/deployment/deployment_type_detector.py +10 -14
- claude_mpm/services/agents/deployment/local_template_deployment.py +6 -3
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +15 -11
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +9 -6
- claude_mpm/services/agents/loading/agent_profile_loader.py +1 -2
- claude_mpm/services/agents/memory/agent_memory_manager.py +27 -27
- claude_mpm/services/agents/memory/content_manager.py +9 -4
- claude_mpm/services/claude_session_logger.py +5 -8
- claude_mpm/services/cli/memory_crud_service.py +1 -1
- claude_mpm/services/cli/memory_output_formatter.py +1 -1
- claude_mpm/services/cli/startup_checker.py +13 -10
- claude_mpm/services/cli/unified_dashboard_manager.py +10 -6
- claude_mpm/services/command_deployment_service.py +9 -7
- claude_mpm/services/core/path_resolver.py +8 -5
- claude_mpm/services/diagnostics/checks/agent_check.py +4 -7
- claude_mpm/services/diagnostics/checks/installation_check.py +19 -16
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +30 -28
- claude_mpm/services/diagnostics/checks/startup_log_check.py +5 -3
- claude_mpm/services/events/core.py +2 -3
- claude_mpm/services/framework_claude_md_generator/content_validator.py +2 -2
- claude_mpm/services/hook_installer_service.py +2 -3
- claude_mpm/services/hook_service.py +5 -6
- claude_mpm/services/mcp_gateway/auto_configure.py +4 -5
- claude_mpm/services/mcp_gateway/main.py +7 -4
- claude_mpm/services/mcp_gateway/server/stdio_server.py +3 -4
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -2
- claude_mpm/services/mcp_service_verifier.py +18 -17
- claude_mpm/services/memory/builder.py +1 -2
- claude_mpm/services/memory/indexed_memory.py +1 -1
- claude_mpm/services/memory/optimizer.py +1 -2
- claude_mpm/services/monitor/daemon_manager.py +3 -3
- claude_mpm/services/monitor/handlers/file.py +5 -4
- claude_mpm/services/monitor/management/lifecycle.py +1 -1
- claude_mpm/services/monitor/server.py +14 -12
- claude_mpm/services/project/architecture_analyzer.py +5 -5
- claude_mpm/services/project/metrics_collector.py +4 -4
- claude_mpm/services/project/project_organizer.py +4 -4
- claude_mpm/services/project/registry.py +9 -3
- claude_mpm/services/shared/config_service_base.py +10 -11
- claude_mpm/services/socketio/handlers/file.py +5 -4
- claude_mpm/services/socketio/handlers/git.py +7 -7
- claude_mpm/services/socketio/server/core.py +10 -10
- claude_mpm/services/subprocess_launcher_service.py +5 -10
- claude_mpm/services/ticket_services/formatter_service.py +1 -1
- claude_mpm/services/ticket_services/validation_service.py +5 -5
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +5 -5
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +4 -4
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +4 -4
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +4 -4
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +4 -4
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +6 -2
- claude_mpm/services/unified/config_strategies/unified_config_service.py +24 -13
- claude_mpm/services/version_control/conflict_resolution.py +6 -2
- claude_mpm/services/version_control/git_operations.py +1 -1
- claude_mpm/services/version_control/version_parser.py +1 -1
- claude_mpm/storage/state_storage.py +3 -3
- claude_mpm/tools/__main__.py +1 -1
- claude_mpm/tools/code_tree_analyzer.py +17 -14
- claude_mpm/tools/socketio_debug.py +7 -7
- claude_mpm/utils/common.py +6 -2
- claude_mpm/utils/config_manager.py +9 -3
- claude_mpm/utils/database_connector.py +4 -4
- claude_mpm/utils/dependency_strategies.py +1 -1
- claude_mpm/utils/environment_context.py +3 -2
- claude_mpm/utils/file_utils.py +1 -2
- claude_mpm/utils/path_operations.py +3 -1
- claude_mpm/utils/robust_installer.py +3 -4
- claude_mpm/validation/frontmatter_validator.py +4 -4
- {claude_mpm-4.6.1.dist-info → claude_mpm-4.7.1.dist-info}/METADATA +1 -1
- {claude_mpm-4.6.1.dist-info → claude_mpm-4.7.1.dist-info}/RECORD +111 -110
- {claude_mpm-4.6.1.dist-info → claude_mpm-4.7.1.dist-info}/WHEEL +0 -0
- {claude_mpm-4.6.1.dist-info → claude_mpm-4.7.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.6.1.dist-info → claude_mpm-4.7.1.dist-info}/licenses/LICENSE +0 -0
- {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("
|
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"
|
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"
|
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",
|
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]
|
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
|
-
|
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
|
-
|
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
|
|
claude_mpm/cli/commands/run.py
CHANGED
@@ -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("
|
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("
|
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("
|
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("
|
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
|
76
|
+
AGENT_NAME: ClassVar[Dict[str, Any]] = {
|
77
|
+
"flags": ["--agent"],
|
78
|
+
"help": "Agent name or pattern",
|
79
|
+
}
|
77
80
|
|
78
|
-
AGENT_DIR
|
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",
|
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
|
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(
|
291
|
+
with Path(file_path).open(mode) as f:
|
292
292
|
return f.read()
|
293
293
|
else:
|
294
|
-
with open(
|
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}")
|
claude_mpm/core/claude_runner.py
CHANGED
@@ -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
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
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
|
claude_mpm/core/container.py
CHANGED
@@ -538,10 +538,16 @@ class DIContainer(IServiceContainer):
|
|
538
538
|
|
539
539
|
elif registration.lifetime == ServiceLifetime.SCOPED:
|
540
540
|
# Check current scope
|
541
|
-
if
|
542
|
-
|
543
|
-
|
544
|
-
|
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)
|
claude_mpm/core/file_utils.py
CHANGED
@@ -173,10 +173,10 @@ def safe_read(
|
|
173
173
|
|
174
174
|
try:
|
175
175
|
if "b" in mode:
|
176
|
-
with open(
|
176
|
+
with Path(filepath).open(mode) as f:
|
177
177
|
return f.read()
|
178
178
|
else:
|
179
|
-
with open(
|
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
|
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(
|
325
|
+
with Path(filepath).open(mode) as f:
|
324
326
|
f.write(content)
|
325
327
|
else:
|
326
|
-
with open(
|
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
|
-
|
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
|
-
|
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(
|
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 =
|
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 =
|
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
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
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 =
|
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
|
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=
|
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("
|
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 =
|
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=
|
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
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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()
|
claude_mpm/core/unified_paths.py
CHANGED
@@ -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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|