claude-mpm 4.6.0__py3-none-any.whl → 4.7.0__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 +2 -3
- 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.0.dist-info → claude_mpm-4.7.0.dist-info}/METADATA +1 -1
- {claude_mpm-4.6.0.dist-info → claude_mpm-4.7.0.dist-info}/RECORD +111 -110
- {claude_mpm-4.6.0.dist-info → claude_mpm-4.7.0.dist-info}/WHEEL +0 -0
- {claude_mpm-4.6.0.dist-info → claude_mpm-4.7.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.6.0.dist-info → claude_mpm-4.7.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.6.0.dist-info → claude_mpm-4.7.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,5 @@
|
|
1
1
|
"""Simple directory listing API with proper filtering to match main code explorer"""
|
2
2
|
|
3
|
-
import os
|
4
3
|
from pathlib import Path
|
5
4
|
|
6
5
|
from aiohttp import web
|
@@ -104,19 +103,19 @@ def has_code_files(directory_path, max_depth=5, current_depth=0):
|
|
104
103
|
return False
|
105
104
|
|
106
105
|
try:
|
107
|
-
for item in
|
106
|
+
for item in list(Path(directory_path).iterdir()):
|
108
107
|
# Skip hidden files/dirs unless in exceptions
|
109
108
|
if item.startswith(".") and item not in DOTFILE_EXCEPTIONS:
|
110
109
|
continue
|
111
110
|
|
112
|
-
item_path =
|
111
|
+
item_path = Path(directory_path) / item
|
113
112
|
|
114
|
-
if
|
113
|
+
if Path(item_path).is_file():
|
115
114
|
# Check if it's a code file
|
116
|
-
ext =
|
115
|
+
ext = Path(item).suffix.lower()
|
117
116
|
if ext in CODE_EXTENSIONS:
|
118
117
|
return True
|
119
|
-
elif
|
118
|
+
elif Path(item_path).is_dir():
|
120
119
|
# Skip certain directories
|
121
120
|
if item in SKIP_DIRS or item.endswith(".egg-info"):
|
122
121
|
continue
|
@@ -153,9 +152,9 @@ def should_show_item(item_name, item_path, is_directory):
|
|
153
152
|
if not has_code_files(item_path, max_depth=3):
|
154
153
|
# Check if it has any visible subdirectories
|
155
154
|
try:
|
156
|
-
for subitem in
|
157
|
-
subitem_path =
|
158
|
-
if
|
155
|
+
for subitem in list(Path(item_path).iterdir()):
|
156
|
+
subitem_path = Path(item_path) / subitem
|
157
|
+
if Path(subitem_path).is_dir():
|
159
158
|
if not subitem.startswith(".") and subitem not in SKIP_DIRS:
|
160
159
|
return True
|
161
160
|
return False
|
@@ -163,7 +162,7 @@ def should_show_item(item_name, item_path, is_directory):
|
|
163
162
|
return False
|
164
163
|
else:
|
165
164
|
# For files, check if it's a code file or documentation
|
166
|
-
ext =
|
165
|
+
ext = Path(item_name).suffix.lower()
|
167
166
|
if ext in CODE_EXTENSIONS:
|
168
167
|
return True
|
169
168
|
|
@@ -189,12 +188,12 @@ async def list_directory(request):
|
|
189
188
|
path = request.query.get("path", ".")
|
190
189
|
|
191
190
|
# Convert to absolute path
|
192
|
-
abs_path =
|
191
|
+
abs_path = Path(Path(path).resolve().expanduser())
|
193
192
|
|
194
193
|
result = {
|
195
194
|
"path": abs_path,
|
196
|
-
"exists":
|
197
|
-
"is_directory":
|
195
|
+
"exists": Path(abs_path).exists(),
|
196
|
+
"is_directory": Path(abs_path).is_dir(),
|
198
197
|
"contents": [],
|
199
198
|
"filtered": True, # Indicate that filtering is applied
|
200
199
|
"filter_info": "Showing only code files and directories with code",
|
@@ -206,11 +205,11 @@ async def list_directory(request):
|
|
206
205
|
gitignore_mgr = GitignoreManager()
|
207
206
|
|
208
207
|
# List all items
|
209
|
-
items =
|
208
|
+
items = list(Path(abs_path).iterdir())
|
210
209
|
|
211
210
|
for item in items:
|
212
|
-
item_path =
|
213
|
-
is_directory =
|
211
|
+
item_path = Path(abs_path) / item
|
212
|
+
is_directory = Path(item_path).is_dir()
|
214
213
|
|
215
214
|
# Check if item should be ignored by gitignore
|
216
215
|
if gitignore_mgr.should_ignore(Path(item_path), Path(abs_path)):
|
@@ -226,7 +225,7 @@ async def list_directory(request):
|
|
226
225
|
"name": item,
|
227
226
|
"path": item_path,
|
228
227
|
"is_directory": is_directory,
|
229
|
-
"is_file":
|
228
|
+
"is_file": Path(item_path).is_file(),
|
230
229
|
"is_code_file": not is_directory
|
231
230
|
and any(item.endswith(ext) for ext in CODE_EXTENSIONS),
|
232
231
|
}
|
@@ -10,6 +10,7 @@ import re
|
|
10
10
|
import subprocess
|
11
11
|
import sys
|
12
12
|
from datetime import datetime, timezone
|
13
|
+
from pathlib import Path
|
13
14
|
from typing import Optional
|
14
15
|
|
15
16
|
# Import tool analysis with fallback for direct execution
|
@@ -309,7 +310,7 @@ class EventHandlers:
|
|
309
310
|
"""Get git branch for the given directory with caching."""
|
310
311
|
# Use current working directory if not specified
|
311
312
|
if not working_dir:
|
312
|
-
working_dir =
|
313
|
+
working_dir = Path.cwd()
|
313
314
|
|
314
315
|
# Check cache first (cache for 30 seconds)
|
315
316
|
current_time = datetime.now(timezone.utc).timestamp()
|
@@ -325,7 +326,7 @@ class EventHandlers:
|
|
325
326
|
# Try to get git branch
|
326
327
|
try:
|
327
328
|
# Change to the working directory temporarily
|
328
|
-
original_cwd =
|
329
|
+
original_cwd = Path.cwd()
|
329
330
|
os.chdir(working_dir)
|
330
331
|
|
331
332
|
# Run git command to get current branch
|
@@ -224,7 +224,7 @@ class HookHandler:
|
|
224
224
|
"""Get git branch for the given directory with caching."""
|
225
225
|
# Use current working directory if not specified
|
226
226
|
if not working_dir:
|
227
|
-
working_dir =
|
227
|
+
working_dir = Path.cwd()
|
228
228
|
|
229
229
|
# Check cache first (cache for 30 seconds)
|
230
230
|
current_time = time.time()
|
@@ -240,7 +240,7 @@ class HookHandler:
|
|
240
240
|
# Try to get git branch
|
241
241
|
try:
|
242
242
|
# Change to the working directory temporarily
|
243
|
-
original_cwd =
|
243
|
+
original_cwd = Path.cwd()
|
244
244
|
os.chdir(working_dir)
|
245
245
|
|
246
246
|
# Run git command to get current branch
|
@@ -313,7 +313,7 @@ class ClaudeHookHandler:
|
|
313
313
|
"""
|
314
314
|
# Use current working directory if not specified
|
315
315
|
if not working_dir:
|
316
|
-
working_dir =
|
316
|
+
working_dir = Path.cwd()
|
317
317
|
|
318
318
|
# Check cache first (cache for 30 seconds)
|
319
319
|
current_time = datetime.now(timezone.utc).timestamp()
|
@@ -329,7 +329,7 @@ class ClaudeHookHandler:
|
|
329
329
|
# Try to get git branch
|
330
330
|
try:
|
331
331
|
# Change to the working directory temporarily
|
332
|
-
original_cwd =
|
332
|
+
original_cwd = Path.cwd()
|
333
333
|
os.chdir(working_dir)
|
334
334
|
|
335
335
|
# Run git command to get current branch
|
@@ -37,7 +37,7 @@ find_claude_mpm() {
|
|
37
37
|
cmd_path=$(readlink -f "$cmd_path")
|
38
38
|
fi
|
39
39
|
# Extract the base directory (usually site-packages or venv)
|
40
|
-
local base_dir=$(python3 -c "import claude_mpm; import os; print(
|
40
|
+
local base_dir=$(python3 -c "import claude_mpm; import os; print(Path(os.path.dirname(claude_mpm.__file__).parent))" 2>/dev/null)
|
41
41
|
if [ -n "$base_dir" ]; then
|
42
42
|
echo "$base_dir"
|
43
43
|
return 0
|
@@ -71,7 +71,7 @@ try:
|
|
71
71
|
import claude_mpm
|
72
72
|
import os
|
73
73
|
# Get the package directory
|
74
|
-
pkg_dir =
|
74
|
+
pkg_dir = Path(claude_mpm.__file__).parent
|
75
75
|
# Check if we're in a development install (src directory)
|
76
76
|
if 'src' in pkg_dir:
|
77
77
|
# Go up to find the project root
|
@@ -81,10 +81,10 @@ try:
|
|
81
81
|
project_root = os.sep.join(parts[:src_idx])
|
82
82
|
print(project_root)
|
83
83
|
else:
|
84
|
-
print(
|
84
|
+
print(Path(os.path.dirname(pkg_dir).parent))
|
85
85
|
else:
|
86
86
|
# Installed package - just return the package location
|
87
|
-
print(
|
87
|
+
print(Path(pkg_dir).parent)
|
88
88
|
except Exception:
|
89
89
|
pass
|
90
90
|
" 2>/dev/null)
|
@@ -99,7 +99,7 @@ except Exception:
|
|
99
99
|
for path_dir in $PATH; do
|
100
100
|
if [ -f "$path_dir/claude-mpm" ]; then
|
101
101
|
# Found claude-mpm executable, try to find its package
|
102
|
-
local pkg_dir=$(cd "$path_dir" && python3 -c "import claude_mpm; import os; print(
|
102
|
+
local pkg_dir=$(cd "$path_dir" && python3 -c "import claude_mpm; import os; print(Path(os.path.dirname(claude_mpm.__file__).parent))" 2>/dev/null)
|
103
103
|
if [ -n "$pkg_dir" ]; then
|
104
104
|
echo "$pkg_dir"
|
105
105
|
return 0
|
@@ -339,8 +339,8 @@ main "$@"
|
|
339
339
|
|
340
340
|
# Make sure it's executable
|
341
341
|
if script_path.exists():
|
342
|
-
st =
|
343
|
-
|
342
|
+
st = Path(script_path).stat()
|
343
|
+
Path(script_path).chmod(st.st_mode | stat.S_IEXEC)
|
344
344
|
self._hook_script_path = script_path
|
345
345
|
return script_path
|
346
346
|
|
@@ -588,9 +588,9 @@ main "$@"
|
|
588
588
|
issues.append(f"Invalid Claude settings JSON: {e}")
|
589
589
|
|
590
590
|
# Check if claude-mpm is accessible
|
591
|
-
|
592
|
-
|
593
|
-
|
591
|
+
import importlib.util
|
592
|
+
|
593
|
+
if importlib.util.find_spec("claude_mpm") is None:
|
594
594
|
issues.append("claude-mpm package not found in Python environment")
|
595
595
|
|
596
596
|
is_valid = len(issues) == 0
|
@@ -10,6 +10,7 @@ import os
|
|
10
10
|
import re
|
11
11
|
import sys
|
12
12
|
from datetime import datetime, timezone
|
13
|
+
from pathlib import Path
|
13
14
|
from typing import Any, Optional
|
14
15
|
|
15
16
|
# Debug mode
|
@@ -64,8 +65,8 @@ class ResponseTrackingManager:
|
|
64
65
|
if config_file:
|
65
66
|
# Use specific config file with ConfigLoader
|
66
67
|
pattern = ConfigPattern(
|
67
|
-
filenames=[
|
68
|
-
search_paths=[
|
68
|
+
filenames=[Path(config_file).name],
|
69
|
+
search_paths=[Path(config_file).parent],
|
69
70
|
env_prefix="CLAUDE_MPM_",
|
70
71
|
)
|
71
72
|
config = config_loader.load_config(
|
@@ -12,6 +12,7 @@ import subprocess
|
|
12
12
|
import time
|
13
13
|
from collections import deque
|
14
14
|
from datetime import datetime, timezone
|
15
|
+
from pathlib import Path
|
15
16
|
from typing import Optional
|
16
17
|
|
17
18
|
# Import constants for configuration
|
@@ -176,7 +177,7 @@ class StateManagerService:
|
|
176
177
|
"""
|
177
178
|
# Use current working directory if not specified
|
178
179
|
if not working_dir:
|
179
|
-
working_dir =
|
180
|
+
working_dir = Path.cwd()
|
180
181
|
|
181
182
|
# Check cache first (cache for 30 seconds)
|
182
183
|
current_time = datetime.now(timezone.utc).timestamp()
|
@@ -192,7 +193,7 @@ class StateManagerService:
|
|
192
193
|
# Try to get git branch
|
193
194
|
try:
|
194
195
|
# Change to the working directory temporarily
|
195
|
-
original_cwd =
|
196
|
+
original_cwd = Path.cwd()
|
196
197
|
os.chdir(working_dir)
|
197
198
|
|
198
199
|
# Run git command to get current branch
|
@@ -125,10 +125,13 @@ class ToolCallInterceptor:
|
|
125
125
|
f"[{result.get('hook_name', 'Unknown')}] {result.get('error')}"
|
126
126
|
)
|
127
127
|
|
128
|
-
if
|
128
|
+
if (
|
129
|
+
result.get("modified")
|
130
|
+
and result.get("data")
|
131
|
+
and "parameters" in result.get("data", {})
|
132
|
+
):
|
129
133
|
# Update parameters if modified
|
130
|
-
|
131
|
-
modified_params = result["data"]["parameters"]
|
134
|
+
modified_params = result["data"]["parameters"]
|
132
135
|
|
133
136
|
if result.get("metadata"):
|
134
137
|
hook_metadata.update(result["metadata"])
|
claude_mpm/scripts/mcp_server.py
CHANGED
@@ -6,15 +6,13 @@ It handles proper Python path setup and error reporting to stderr.
|
|
6
6
|
"""
|
7
7
|
|
8
8
|
import logging
|
9
|
-
import os
|
10
9
|
import sys
|
10
|
+
from pathlib import Path
|
11
11
|
|
12
12
|
# Since we're now in src/claude_mpm/scripts/, we need to go up 3 levels to reach the project root
|
13
13
|
# Then down into src to add it to the path
|
14
|
-
project_root =
|
15
|
-
|
16
|
-
)
|
17
|
-
sys.path.insert(0, os.path.join(project_root, "src"))
|
14
|
+
project_root = Path(Path(Path(__file__).parent.resolve().joinpath(), "..", "..", ".."))
|
15
|
+
sys.path.insert(0, Path(project_root) / "src")
|
18
16
|
|
19
17
|
|
20
18
|
def setup_logging():
|
@@ -12,7 +12,7 @@ import json
|
|
12
12
|
import re
|
13
13
|
from datetime import datetime, timezone
|
14
14
|
from pathlib import Path
|
15
|
-
from typing import Any, Dict, List, Optional, Tuple
|
15
|
+
from typing import Any, ClassVar, Dict, List, Optional, Tuple
|
16
16
|
|
17
17
|
from claude_mpm.core.exceptions import AgentDeploymentError
|
18
18
|
from claude_mpm.core.logging_config import get_logger
|
@@ -22,13 +22,13 @@ class AgentBuilderService:
|
|
22
22
|
"""Service for building and managing agent configurations."""
|
23
23
|
|
24
24
|
# Valid agent models
|
25
|
-
VALID_MODELS = ["sonnet", "opus", "haiku"]
|
25
|
+
VALID_MODELS: ClassVar[list[str]] = ["sonnet", "opus", "haiku"]
|
26
26
|
|
27
27
|
# Valid tool choices
|
28
|
-
VALID_TOOL_CHOICES = ["auto", "required", "any", "none"]
|
28
|
+
VALID_TOOL_CHOICES: ClassVar[list[str]] = ["auto", "required", "any", "none"]
|
29
29
|
|
30
30
|
# Agent categories
|
31
|
-
AGENT_CATEGORIES = [
|
31
|
+
AGENT_CATEGORIES: ClassVar[list[str]] = [
|
32
32
|
"engineering",
|
33
33
|
"qa",
|
34
34
|
"documentation",
|
@@ -63,13 +63,11 @@ class DeploymentTypeDetector:
|
|
63
63
|
"""
|
64
64
|
# Check if we're in a project directory with .claude-mpm/agents
|
65
65
|
project_agents_dir = working_directory / ".claude-mpm" / "agents"
|
66
|
-
if project_agents_dir.exists():
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
except Exception:
|
72
|
-
pass
|
66
|
+
if project_agents_dir.exists() and templates_dir and templates_dir.exists():
|
67
|
+
try:
|
68
|
+
return project_agents_dir.resolve() == templates_dir.resolve()
|
69
|
+
except Exception:
|
70
|
+
pass
|
73
71
|
return False
|
74
72
|
|
75
73
|
@staticmethod
|
@@ -86,13 +84,11 @@ class DeploymentTypeDetector:
|
|
86
84
|
True if deploying user custom agents, False otherwise
|
87
85
|
"""
|
88
86
|
user_agents_dir = Path.home() / ".claude-mpm" / "agents"
|
89
|
-
if user_agents_dir.exists():
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
except Exception:
|
95
|
-
pass
|
87
|
+
if user_agents_dir.exists() and templates_dir and templates_dir.exists():
|
88
|
+
try:
|
89
|
+
return user_agents_dir.resolve() == templates_dir.resolve()
|
90
|
+
except Exception:
|
91
|
+
pass
|
96
92
|
return False
|
97
93
|
|
98
94
|
@staticmethod
|
@@ -330,9 +330,12 @@ class LocalTemplateDeploymentService:
|
|
330
330
|
for agent_id, template in templates.items():
|
331
331
|
if agent_id in deployed:
|
332
332
|
# Check if needs update
|
333
|
-
if deployed[agent_id][
|
334
|
-
|
335
|
-
|
333
|
+
if deployed[agent_id][
|
334
|
+
"version"
|
335
|
+
] != template.agent_version and self.deploy_single_local_template(
|
336
|
+
agent_id, force_rebuild=True
|
337
|
+
):
|
338
|
+
results["updated"].append(agent_id)
|
336
339
|
# New agent to deploy
|
337
340
|
elif self.deploy_single_local_template(agent_id):
|
338
341
|
results["added"].append(agent_id)
|
@@ -209,13 +209,15 @@ class MultiSourceAgentDeploymentService:
|
|
209
209
|
f"User agent '{agent_name}' v{other_agent['version']} "
|
210
210
|
f"overridden by higher {highest_version_agent['source']} version v{highest_version_agent['version']}"
|
211
211
|
)
|
212
|
-
elif
|
212
|
+
elif (
|
213
|
+
version_comparison == 0
|
214
|
+
and other_agent["source"] != highest_version_agent["source"]
|
215
|
+
):
|
213
216
|
# Log info when versions are equal but different sources
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
)
|
217
|
+
self.logger.info(
|
218
|
+
f"Using {highest_version_agent['source']} source for '{agent_name}' "
|
219
|
+
f"(same version v{highest_version_agent['version']} as {other_agent['source']} source)"
|
220
|
+
)
|
219
221
|
|
220
222
|
return selected_agents
|
221
223
|
|
@@ -850,11 +852,13 @@ class MultiSourceAgentDeploymentService:
|
|
850
852
|
):
|
851
853
|
# In development mode, unknown agents are likely system agents being tested
|
852
854
|
return "system"
|
853
|
-
if
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
855
|
+
if (
|
856
|
+
deployment_context == DeploymentContext.PIPX_INSTALL
|
857
|
+
and agent_name.count("-") <= 2
|
858
|
+
and len(agent_name) <= 20
|
859
|
+
):
|
860
|
+
# In pipx mode, check if agent follows system naming patterns
|
861
|
+
return "system"
|
858
862
|
except Exception:
|
859
863
|
pass
|
860
864
|
|
@@ -70,12 +70,15 @@ class SystemInstructionsDeployer:
|
|
70
70
|
target_file = claude_dir / target_name
|
71
71
|
|
72
72
|
# Check if update needed
|
73
|
-
if
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
73
|
+
if (
|
74
|
+
not force_rebuild
|
75
|
+
and target_file.exists()
|
76
|
+
and target_file.stat().st_mtime >= source_path.stat().st_mtime
|
77
|
+
):
|
78
|
+
# File is up to date based on modification time
|
79
|
+
results["skipped"].append(target_name)
|
80
|
+
self.logger.debug(f"Framework file {target_name} up to date")
|
81
|
+
continue
|
79
82
|
|
80
83
|
# Read and deploy framework file
|
81
84
|
file_content = source_path.read_text()
|
@@ -21,7 +21,6 @@ multi-file implementation for better maintainability.
|
|
21
21
|
|
22
22
|
import asyncio
|
23
23
|
import json
|
24
|
-
import os
|
25
24
|
from dataclasses import dataclass, field
|
26
25
|
from datetime import datetime, timezone
|
27
26
|
from enum import Enum
|
@@ -113,7 +112,7 @@ class AgentProfileLoader(BaseService):
|
|
113
112
|
super().__init__(name="agent_profile_loader", config=config)
|
114
113
|
|
115
114
|
# Core configuration
|
116
|
-
self.working_directory = Path(
|
115
|
+
self.working_directory = Path(Path.cwd())
|
117
116
|
self.framework_path = self._detect_framework_path()
|
118
117
|
self.user_home = Path.home()
|
119
118
|
|
@@ -22,8 +22,7 @@ following the naming convention: {agent_id}_memories.md
|
|
22
22
|
"""
|
23
23
|
|
24
24
|
import logging
|
25
|
-
import
|
26
|
-
from typing import Any, Dict, List, Optional, Tuple
|
25
|
+
from typing import Any, ClassVar, Dict, List, Optional, Tuple
|
27
26
|
|
28
27
|
from claude_mpm.core.config import Config
|
29
28
|
from claude_mpm.core.interfaces import MemoryServiceInterface
|
@@ -54,7 +53,7 @@ class AgentMemoryManager(MemoryServiceInterface):
|
|
54
53
|
|
55
54
|
# Default limits - will be overridden by configuration
|
56
55
|
# Updated to support 20k tokens (~80KB) for enhanced memory capacity
|
57
|
-
DEFAULT_MEMORY_LIMITS = {
|
56
|
+
DEFAULT_MEMORY_LIMITS: ClassVar[dict[str, int]] = {
|
58
57
|
"max_file_size_kb": 80, # Increased from 8KB to 80KB (20k tokens)
|
59
58
|
"max_items": 100, # Maximum total memory items
|
60
59
|
"max_line_length": 120,
|
@@ -78,7 +77,7 @@ class AgentMemoryManager(MemoryServiceInterface):
|
|
78
77
|
self.config = config or Config()
|
79
78
|
self.project_root = get_path_manager().project_root
|
80
79
|
# Use current working directory by default, not project root
|
81
|
-
self.working_directory = working_directory or Path(
|
80
|
+
self.working_directory = working_directory or Path(Path.cwd())
|
82
81
|
|
83
82
|
# Use only project memory directory
|
84
83
|
self.project_memories_dir = self.working_directory / ".claude-mpm" / "memories"
|
@@ -387,31 +386,32 @@ class AgentMemoryManager(MemoryServiceInterface):
|
|
387
386
|
memory_items = data["Remember"]
|
388
387
|
|
389
388
|
# Process memory items if found and not null
|
390
|
-
if
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
389
|
+
if (
|
390
|
+
memory_items is not None
|
391
|
+
and memory_items != "null"
|
392
|
+
and isinstance(memory_items, list)
|
393
|
+
and len(memory_items) > 0
|
394
|
+
):
|
395
|
+
# Filter out empty strings and None values
|
396
|
+
valid_items = []
|
397
|
+
for item in memory_items:
|
398
|
+
if item and isinstance(item, str) and item.strip():
|
399
|
+
valid_items.append(item.strip())
|
400
|
+
|
401
|
+
# Only proceed if we have valid items
|
402
|
+
if valid_items:
|
403
|
+
self.logger.info(
|
404
|
+
f"Found {len(valid_items)} memory items for {agent_id}: {valid_items[:2]}..."
|
405
|
+
)
|
406
|
+
success = self._add_learnings_to_memory(
|
407
|
+
agent_id, valid_items
|
408
|
+
)
|
409
|
+
if success:
|
401
410
|
self.logger.info(
|
402
|
-
f"
|
403
|
-
)
|
404
|
-
success = self._add_learnings_to_memory(
|
405
|
-
agent_id, valid_items
|
406
|
-
)
|
407
|
-
if success:
|
408
|
-
self.logger.info(
|
409
|
-
f"Successfully saved {len(valid_items)} memories for {agent_id} to project directory"
|
410
|
-
)
|
411
|
-
return True
|
412
|
-
self.logger.error(
|
413
|
-
f"Failed to save memories for {agent_id}"
|
411
|
+
f"Successfully saved {len(valid_items)} memories for {agent_id} to project directory"
|
414
412
|
)
|
413
|
+
return True
|
414
|
+
self.logger.error(f"Failed to save memories for {agent_id}")
|
415
415
|
|
416
416
|
except json.JSONDecodeError as je:
|
417
417
|
# Not valid JSON, continue to next match
|
@@ -363,10 +363,15 @@ class MemoryContentManager:
|
|
363
363
|
|
364
364
|
# Additional check: if one string contains the other (substring match)
|
365
365
|
# This catches cases where one item is a more detailed version of another
|
366
|
-
if
|
367
|
-
|
368
|
-
|
369
|
-
|
366
|
+
if (
|
367
|
+
len(str1_normalized) > 20
|
368
|
+
and len(str2_normalized) > 20
|
369
|
+
and (
|
370
|
+
str1_normalized in str2_normalized or str2_normalized in str1_normalized
|
371
|
+
)
|
372
|
+
):
|
373
|
+
# Boost similarity for substring matches
|
374
|
+
similarity = max(similarity, 0.85)
|
370
375
|
|
371
376
|
return similarity
|
372
377
|
|
@@ -10,6 +10,8 @@ Now with optional async logging support for improved performance.
|
|
10
10
|
Configuration via .claude-mpm/configuration.yaml.
|
11
11
|
"""
|
12
12
|
|
13
|
+
# Try to import async logger for performance optimization
|
14
|
+
import importlib.util
|
13
15
|
import json
|
14
16
|
import os
|
15
17
|
from datetime import datetime, timezone
|
@@ -22,16 +24,11 @@ from claude_mpm.core.config import Config
|
|
22
24
|
# Import centralized session manager
|
23
25
|
from claude_mpm.services.session_manager import get_session_manager
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
from claude_mpm.services.async_session_logger import (
|
28
|
-
AsyncSessionLogger,
|
29
|
-
get_async_logger,
|
30
|
-
log_response_async,
|
31
|
-
)
|
27
|
+
if importlib.util.find_spec("claude_mpm.services.async_session_logger"):
|
28
|
+
from claude_mpm.services.async_session_logger import get_async_logger
|
32
29
|
|
33
30
|
ASYNC_AVAILABLE = True
|
34
|
-
|
31
|
+
else:
|
35
32
|
ASYNC_AVAILABLE = False
|
36
33
|
|
37
34
|
from claude_mpm.core.logging_utils import get_logger
|
@@ -142,7 +142,7 @@ class MemoryCRUDService(IMemoryCRUDService):
|
|
142
142
|
config_loader = ConfigLoader()
|
143
143
|
config = config_loader.load_main_config()
|
144
144
|
# Use CLAUDE_MPM_USER_PWD if available
|
145
|
-
user_pwd = os.environ.get("CLAUDE_MPM_USER_PWD",
|
145
|
+
user_pwd = os.environ.get("CLAUDE_MPM_USER_PWD", Path.cwd())
|
146
146
|
current_dir = Path(user_pwd)
|
147
147
|
self._memory_manager = AgentMemoryManager(config, current_dir)
|
148
148
|
return self._memory_manager
|
@@ -90,7 +90,7 @@ class MemoryOutputFormatter(IMemoryOutputFormatter):
|
|
90
90
|
"success": "✅" if not quiet else "[OK]",
|
91
91
|
"error": "❌" if not quiet else "[ERROR]",
|
92
92
|
"warning": "⚠️" if not quiet else "[WARN]",
|
93
|
-
"info": "
|
93
|
+
"info": "[INFO]️" if not quiet else "[INFO]",
|
94
94
|
"memory": "🧠" if not quiet else "[MEMORY]",
|
95
95
|
"file": "📁" if not quiet else "[FILE]",
|
96
96
|
"agent": "🤖" if not quiet else "[AGENT]",
|