claude-mpm 5.4.21__py3-none-any.whl → 5.4.36__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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_AGENT.md +164 -0
- claude_mpm/agents/BASE_ENGINEER.md +658 -0
- claude_mpm/agents/MEMORY.md +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +320 -880
- claude_mpm/agents/WORKFLOW.md +5 -254
- claude_mpm/agents/agent_loader.py +1 -1
- claude_mpm/agents/base_agent.json +31 -0
- claude_mpm/cli/commands/agent_state_manager.py +10 -10
- claude_mpm/cli/commands/agents.py +9 -9
- claude_mpm/cli/commands/auto_configure.py +4 -4
- claude_mpm/cli/commands/configure.py +1 -1
- claude_mpm/cli/commands/postmortem.py +1 -1
- claude_mpm/cli/interactive/agent_wizard.py +2 -2
- claude_mpm/cli/startup.py +98 -58
- claude_mpm/core/config.py +2 -4
- claude_mpm/core/framework/loaders/agent_loader.py +1 -1
- claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
- claude_mpm/core/unified_agent_registry.py +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
- claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
- claude_mpm/dashboard/static/svelte-build/index.html +36 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +149 -1
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +26 -6
- claude_mpm/models/git_repository.py +3 -3
- claude_mpm/services/agents/cache_git_manager.py +6 -6
- claude_mpm/services/agents/deployment/agent_deployment.py +7 -7
- claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -2
- claude_mpm/services/agents/deployment/agent_template_builder.py +2 -2
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +20 -22
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +55 -53
- claude_mpm/services/agents/git_source_manager.py +2 -2
- claude_mpm/services/agents/recommender.py +5 -3
- claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
- claude_mpm/services/agents/sources/git_source_sync_service.py +5 -5
- claude_mpm/services/agents/startup_sync.py +22 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
- claude_mpm/services/git/git_operations_service.py +8 -8
- claude_mpm/services/monitor/server.py +473 -3
- claude_mpm/services/socketio/dashboard_server.py +1 -0
- claude_mpm/services/socketio/event_normalizer.py +37 -6
- claude_mpm/services/socketio/server/core.py +262 -123
- claude_mpm/utils/agent_dependency_loader.py +14 -2
- claude_mpm/utils/agent_filters.py +1 -1
- claude_mpm/utils/migration.py +4 -4
- claude_mpm/utils/robust_installer.py +47 -3
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/METADATA +5 -3
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/RECORD +84 -49
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.36.dist-info}/top_level.txt +0 -0
|
@@ -32,7 +32,6 @@ except ImportError:
|
|
|
32
32
|
# Import VersionService for dynamic version retrieval
|
|
33
33
|
import contextlib
|
|
34
34
|
|
|
35
|
-
import claude_mpm
|
|
36
35
|
from claude_mpm.services.version_service import VersionService
|
|
37
36
|
|
|
38
37
|
from ....core.constants import SystemLimits, TimeoutConfig
|
|
@@ -277,9 +276,16 @@ class SocketIOServerCore:
|
|
|
277
276
|
# Extract event data from payload (handles both direct and wrapped formats)
|
|
278
277
|
# ConnectionManagerService sends: {"namespace": "...", "event": "...", "data": {...}}
|
|
279
278
|
# Direct hook events may send data directly
|
|
280
|
-
|
|
279
|
+
# CRITICAL: Check if payload has the expected event structure (type, subtype, timestamp)
|
|
280
|
+
# If it does, use it directly. Only extract 'data' field if it's a wrapper object.
|
|
281
|
+
if "type" in payload and "subtype" in payload:
|
|
282
|
+
# Payload is already in normalized format, use it directly
|
|
283
|
+
event_data = payload
|
|
284
|
+
elif "data" in payload and isinstance(payload.get("data"), dict):
|
|
285
|
+
# Payload is a wrapper with 'data' field (from ConnectionManagerService)
|
|
281
286
|
event_data = payload["data"]
|
|
282
287
|
else:
|
|
288
|
+
# Fallback: use entire payload
|
|
283
289
|
event_data = payload
|
|
284
290
|
|
|
285
291
|
# Log receipt with more detail
|
|
@@ -409,9 +415,23 @@ class SocketIOServerCore:
|
|
|
409
415
|
self.event_buffer.append(event_data)
|
|
410
416
|
self.stats["events_buffered"] = len(self.event_buffer)
|
|
411
417
|
|
|
412
|
-
# Add to main server's event history
|
|
413
|
-
|
|
418
|
+
# Add to main server's event history UNCONDITIONALLY
|
|
419
|
+
# WHY: event_history is always initialized in SocketIOServer.__init__
|
|
420
|
+
# This ensures events persist for new clients who connect later
|
|
421
|
+
if self.main_server and hasattr(
|
|
422
|
+
self.main_server, "event_history"
|
|
423
|
+
):
|
|
414
424
|
self.main_server.event_history.append(event_data)
|
|
425
|
+
self.logger.debug(
|
|
426
|
+
f"Added to history (total: {len(self.main_server.event_history)})"
|
|
427
|
+
)
|
|
428
|
+
else:
|
|
429
|
+
# CRITICAL: Log warning if event_history is not available
|
|
430
|
+
# This indicates a configuration or initialization problem
|
|
431
|
+
self.logger.warning(
|
|
432
|
+
"event_history not initialized on main_server! "
|
|
433
|
+
"Events will not persist for new clients."
|
|
434
|
+
)
|
|
415
435
|
|
|
416
436
|
# Use the broadcaster's sio to emit (it's the same as self.sio)
|
|
417
437
|
# This ensures the event goes through the proper channels
|
|
@@ -445,6 +465,21 @@ class SocketIOServerCore:
|
|
|
445
465
|
self.event_buffer.append(event_data)
|
|
446
466
|
self.stats["events_buffered"] = len(self.event_buffer)
|
|
447
467
|
|
|
468
|
+
# Add to main server's event history (fallback path)
|
|
469
|
+
# WHY: Ensure events persist even when broadcaster is unavailable
|
|
470
|
+
if self.main_server and hasattr(
|
|
471
|
+
self.main_server, "event_history"
|
|
472
|
+
):
|
|
473
|
+
self.main_server.event_history.append(event_data)
|
|
474
|
+
self.logger.debug(
|
|
475
|
+
f"Added to history via fallback (total: {len(self.main_server.event_history)})"
|
|
476
|
+
)
|
|
477
|
+
else:
|
|
478
|
+
self.logger.warning(
|
|
479
|
+
"event_history not initialized on main_server (fallback path)! "
|
|
480
|
+
"Events will not persist for new clients."
|
|
481
|
+
)
|
|
482
|
+
|
|
448
483
|
# Return 204 No Content for success
|
|
449
484
|
self.logger.debug(f"✅ HTTP event processed successfully: {event_type}")
|
|
450
485
|
return web.Response(status=204)
|
|
@@ -504,7 +539,7 @@ class SocketIOServerCore:
|
|
|
504
539
|
from pathlib import Path
|
|
505
540
|
|
|
506
541
|
try:
|
|
507
|
-
working_dir = Path.cwd()
|
|
542
|
+
working_dir = str(Path.cwd())
|
|
508
543
|
home_dir = str(Path.home())
|
|
509
544
|
|
|
510
545
|
return web.json_response(
|
|
@@ -594,6 +629,169 @@ class SocketIOServerCore:
|
|
|
594
629
|
self.app.router.add_get("/api/file/read", file_read_handler)
|
|
595
630
|
self.logger.info("✅ File reading API registered at /api/file/read")
|
|
596
631
|
|
|
632
|
+
# Add files listing endpoint for file browser
|
|
633
|
+
async def files_list_handler(request):
|
|
634
|
+
"""Handle GET /api/files for listing files in working directory."""
|
|
635
|
+
|
|
636
|
+
try:
|
|
637
|
+
# Get working directory from query params or use current directory
|
|
638
|
+
working_dir = request.query.get("path", str(Path.cwd()))
|
|
639
|
+
abs_working_dir = Path(working_dir).resolve().expanduser()
|
|
640
|
+
|
|
641
|
+
# Security check - ensure directory is accessible
|
|
642
|
+
if not abs_working_dir.exists():
|
|
643
|
+
return web.json_response(
|
|
644
|
+
{"error": "Directory not found"}, status=404
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
if not abs_working_dir.is_dir():
|
|
648
|
+
return web.json_response(
|
|
649
|
+
{"error": "Path is not a directory"}, status=400
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
# Collect files and directories
|
|
653
|
+
files = []
|
|
654
|
+
directories = []
|
|
655
|
+
|
|
656
|
+
# Common patterns to exclude
|
|
657
|
+
exclude_patterns = {
|
|
658
|
+
".git",
|
|
659
|
+
".venv",
|
|
660
|
+
"venv",
|
|
661
|
+
"node_modules",
|
|
662
|
+
"__pycache__",
|
|
663
|
+
".pytest_cache",
|
|
664
|
+
".mypy_cache",
|
|
665
|
+
"dist",
|
|
666
|
+
"build",
|
|
667
|
+
".next",
|
|
668
|
+
"coverage",
|
|
669
|
+
".coverage",
|
|
670
|
+
".tox",
|
|
671
|
+
".eggs",
|
|
672
|
+
"*.egg-info",
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
for entry in abs_working_dir.iterdir():
|
|
676
|
+
# Skip hidden files and excluded patterns
|
|
677
|
+
if entry.name.startswith("."):
|
|
678
|
+
# Allow .py, .ts, .md, etc. files but skip directories like .git
|
|
679
|
+
if entry.is_dir():
|
|
680
|
+
continue
|
|
681
|
+
|
|
682
|
+
# Skip excluded directories
|
|
683
|
+
if entry.name in exclude_patterns:
|
|
684
|
+
continue
|
|
685
|
+
|
|
686
|
+
try:
|
|
687
|
+
stat_info = entry.stat()
|
|
688
|
+
entry_data = {
|
|
689
|
+
"name": entry.name,
|
|
690
|
+
"path": str(entry),
|
|
691
|
+
"type": "directory" if entry.is_dir() else "file",
|
|
692
|
+
"size": stat_info.st_size if entry.is_file() else 0,
|
|
693
|
+
"modified": stat_info.st_mtime,
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if entry.is_dir():
|
|
697
|
+
directories.append(entry_data)
|
|
698
|
+
else:
|
|
699
|
+
# Add file extension for syntax highlighting
|
|
700
|
+
entry_data["extension"] = entry.suffix.lower()
|
|
701
|
+
files.append(entry_data)
|
|
702
|
+
except (PermissionError, OSError):
|
|
703
|
+
# Skip files we can't access
|
|
704
|
+
continue
|
|
705
|
+
|
|
706
|
+
# Sort directories and files alphabetically
|
|
707
|
+
directories.sort(key=lambda x: x["name"].lower())
|
|
708
|
+
files.sort(key=lambda x: x["name"].lower())
|
|
709
|
+
|
|
710
|
+
return web.json_response(
|
|
711
|
+
{
|
|
712
|
+
"success": True,
|
|
713
|
+
"path": str(abs_working_dir),
|
|
714
|
+
"directories": directories,
|
|
715
|
+
"files": files,
|
|
716
|
+
"total_files": len(files),
|
|
717
|
+
"total_directories": len(directories),
|
|
718
|
+
}
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
except Exception as e:
|
|
722
|
+
self.logger.error(f"Error listing files: {e}")
|
|
723
|
+
return web.json_response({"error": str(e)}, status=500)
|
|
724
|
+
|
|
725
|
+
self.app.router.add_get("/api/files", files_list_handler)
|
|
726
|
+
self.logger.info("✅ Files listing API registered at /api/files")
|
|
727
|
+
|
|
728
|
+
# Add git history endpoint
|
|
729
|
+
async def git_history_handler(request):
|
|
730
|
+
"""Handle POST /api/git-history for getting file git history."""
|
|
731
|
+
import subprocess
|
|
732
|
+
|
|
733
|
+
try:
|
|
734
|
+
# Parse JSON body
|
|
735
|
+
data = await request.json()
|
|
736
|
+
file_path = data.get("path", "")
|
|
737
|
+
limit = data.get("limit", 10)
|
|
738
|
+
|
|
739
|
+
if not file_path:
|
|
740
|
+
return web.json_response(
|
|
741
|
+
{"success": False, "error": "No path provided", "commits": []},
|
|
742
|
+
status=400,
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
abs_path = Path(Path(file_path).resolve().expanduser())
|
|
746
|
+
|
|
747
|
+
if not Path(abs_path).exists():
|
|
748
|
+
return web.json_response(
|
|
749
|
+
{"success": False, "error": "File not found", "commits": []},
|
|
750
|
+
status=404,
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
# Get git log for file
|
|
754
|
+
result = subprocess.run(
|
|
755
|
+
[
|
|
756
|
+
"git",
|
|
757
|
+
"log",
|
|
758
|
+
f"-{limit}",
|
|
759
|
+
"--pretty=format:%H|%an|%ar|%s",
|
|
760
|
+
"--",
|
|
761
|
+
abs_path,
|
|
762
|
+
],
|
|
763
|
+
check=False,
|
|
764
|
+
capture_output=True,
|
|
765
|
+
text=True,
|
|
766
|
+
cwd=Path(abs_path).parent,
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
commits = []
|
|
770
|
+
if result.returncode == 0 and result.stdout:
|
|
771
|
+
for line in result.stdout.strip().split("\n"):
|
|
772
|
+
if line:
|
|
773
|
+
parts = line.split("|", 3)
|
|
774
|
+
if len(parts) == 4:
|
|
775
|
+
commits.append(
|
|
776
|
+
{
|
|
777
|
+
"hash": parts[0][:7], # Short hash
|
|
778
|
+
"author": parts[1],
|
|
779
|
+
"date": parts[2],
|
|
780
|
+
"message": parts[3],
|
|
781
|
+
}
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
return web.json_response({"success": True, "commits": commits})
|
|
785
|
+
|
|
786
|
+
except Exception as e:
|
|
787
|
+
self.logger.error(f"Error getting git history: {e}")
|
|
788
|
+
return web.json_response(
|
|
789
|
+
{"success": False, "error": str(e), "commits": []}, status=500
|
|
790
|
+
)
|
|
791
|
+
|
|
792
|
+
self.app.router.add_post("/api/git-history", git_history_handler)
|
|
793
|
+
self.logger.info("✅ Git history API registered at /api/git-history")
|
|
794
|
+
|
|
597
795
|
def _setup_directory_api(self):
|
|
598
796
|
"""Setup simple directory listing API.
|
|
599
797
|
|
|
@@ -611,7 +809,7 @@ class SocketIOServerCore:
|
|
|
611
809
|
self.logger.error(f"Failed to setup directory API: {e}")
|
|
612
810
|
|
|
613
811
|
def _setup_static_files(self):
|
|
614
|
-
"""Setup static file serving for the dashboard."""
|
|
812
|
+
"""Setup static file serving for the Svelte dashboard."""
|
|
615
813
|
try:
|
|
616
814
|
# Add debug logging for deployment context
|
|
617
815
|
try:
|
|
@@ -624,65 +822,43 @@ class SocketIOServerCore:
|
|
|
624
822
|
except Exception as e:
|
|
625
823
|
self.logger.debug(f"Could not detect deployment context: {e}")
|
|
626
824
|
|
|
627
|
-
|
|
825
|
+
# Find Svelte build directory
|
|
826
|
+
svelte_build_path = self._find_static_path()
|
|
628
827
|
|
|
629
|
-
if
|
|
630
|
-
self.logger.info(f"✅
|
|
828
|
+
if svelte_build_path and svelte_build_path.exists():
|
|
829
|
+
self.logger.info(f"✅ Svelte dashboard found at: {svelte_build_path}")
|
|
830
|
+
self.dashboard_path = svelte_build_path
|
|
631
831
|
|
|
632
|
-
# Serve index.html at root
|
|
832
|
+
# Serve Svelte index.html at root
|
|
633
833
|
async def index_handler(request):
|
|
634
|
-
index_file =
|
|
834
|
+
index_file = svelte_build_path / "index.html"
|
|
635
835
|
if index_file.exists():
|
|
636
|
-
self.logger.debug(
|
|
836
|
+
self.logger.debug(
|
|
837
|
+
f"Serving Svelte dashboard from: {index_file}"
|
|
838
|
+
)
|
|
637
839
|
return web.FileResponse(index_file)
|
|
638
|
-
self.logger.warning(
|
|
639
|
-
f"Dashboard index.html not found at: {index_file}"
|
|
640
|
-
)
|
|
840
|
+
self.logger.warning(f"Svelte index.html not found at: {index_file}")
|
|
641
841
|
return web.Response(text="Dashboard not available", status=404)
|
|
642
842
|
|
|
643
843
|
self.app.router.add_get("/", index_handler)
|
|
644
844
|
|
|
645
|
-
# Serve
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
if dashboard_template.exists():
|
|
651
|
-
self.logger.debug(
|
|
652
|
-
f"Serving dashboard template from: {dashboard_template}"
|
|
653
|
-
)
|
|
654
|
-
return web.FileResponse(dashboard_template)
|
|
655
|
-
# Fallback to the main index if template doesn't exist
|
|
656
|
-
self.logger.warning(
|
|
657
|
-
f"Dashboard template not found at: {dashboard_template}, falling back to index"
|
|
845
|
+
# Serve Svelte app assets at /_app/ (needed for SvelteKit builds)
|
|
846
|
+
svelte_app_path = svelte_build_path / "_app"
|
|
847
|
+
if svelte_app_path.exists():
|
|
848
|
+
self.app.router.add_static(
|
|
849
|
+
"/_app/", svelte_app_path, name="svelte_app"
|
|
658
850
|
)
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
self.app.router.add_get("/dashboard", dashboard_handler)
|
|
662
|
-
|
|
663
|
-
# Serve simple code view template at /code-simple
|
|
664
|
-
async def code_simple_handler(request):
|
|
665
|
-
code_simple_template = (
|
|
666
|
-
self.dashboard_path.parent / "templates" / "code_simple.html"
|
|
851
|
+
self.logger.info(
|
|
852
|
+
f"✅ Svelte dashboard available at http://{self.host}:{self.port}/ (build: {svelte_build_path})"
|
|
667
853
|
)
|
|
668
|
-
|
|
669
|
-
self.logger.debug(
|
|
670
|
-
f"Serving code simple template from: {code_simple_template}"
|
|
671
|
-
)
|
|
672
|
-
return web.FileResponse(code_simple_template)
|
|
673
|
-
# Return error if template doesn't exist
|
|
854
|
+
else:
|
|
674
855
|
self.logger.warning(
|
|
675
|
-
f"
|
|
676
|
-
)
|
|
677
|
-
return web.Response(
|
|
678
|
-
text="Simple code view not available", status=404
|
|
856
|
+
f"⚠️ Svelte _app directory not found at: {svelte_app_path}"
|
|
679
857
|
)
|
|
680
858
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
# Serve version.json from dashboard directory
|
|
859
|
+
# Serve version.json from Svelte build directory
|
|
684
860
|
async def version_handler(request):
|
|
685
|
-
version_file =
|
|
861
|
+
version_file = svelte_build_path / "version.json"
|
|
686
862
|
if version_file.exists():
|
|
687
863
|
self.logger.debug(f"Serving version.json from: {version_file}")
|
|
688
864
|
return web.FileResponse(version_file)
|
|
@@ -698,55 +874,10 @@ class SocketIOServerCore:
|
|
|
698
874
|
|
|
699
875
|
self.app.router.add_get("/version.json", version_handler)
|
|
700
876
|
|
|
701
|
-
# Serve static assets (CSS, JS) from the dashboard static directory
|
|
702
|
-
# Use package-relative path (works for both dev and installed package)
|
|
703
|
-
package_root = Path(claude_mpm.__file__).parent
|
|
704
|
-
dashboard_static_path = package_root / "dashboard" / "static"
|
|
705
|
-
if dashboard_static_path.exists():
|
|
706
|
-
self.app.router.add_static(
|
|
707
|
-
"/static/", dashboard_static_path, name="dashboard_static"
|
|
708
|
-
)
|
|
709
|
-
self.logger.info(
|
|
710
|
-
f"✅ Static assets available at: {dashboard_static_path}"
|
|
711
|
-
)
|
|
712
|
-
else:
|
|
713
|
-
self.logger.warning(
|
|
714
|
-
f"⚠️ Static assets directory not found at: {dashboard_static_path}"
|
|
715
|
-
)
|
|
716
|
-
|
|
717
|
-
# Serve Svelte dashboard build
|
|
718
|
-
svelte_build_path = (
|
|
719
|
-
package_root / "dashboard" / "static" / "svelte-build"
|
|
720
|
-
)
|
|
721
|
-
if svelte_build_path.exists():
|
|
722
|
-
# Serve Svelte dashboard at /svelte route
|
|
723
|
-
async def svelte_handler(request):
|
|
724
|
-
svelte_index = svelte_build_path / "index.html"
|
|
725
|
-
if svelte_index.exists():
|
|
726
|
-
self.logger.debug(
|
|
727
|
-
f"Serving Svelte dashboard from: {svelte_index}"
|
|
728
|
-
)
|
|
729
|
-
return web.FileResponse(svelte_index)
|
|
730
|
-
return web.Response(
|
|
731
|
-
text="Svelte dashboard not available", status=404
|
|
732
|
-
)
|
|
733
|
-
|
|
734
|
-
self.app.router.add_get("/svelte", svelte_handler)
|
|
735
|
-
|
|
736
|
-
# Serve Svelte app assets at /_app/ (needed for SvelteKit builds)
|
|
737
|
-
svelte_app_path = svelte_build_path / "_app"
|
|
738
|
-
if svelte_app_path.exists():
|
|
739
|
-
self.app.router.add_static(
|
|
740
|
-
"/_app/", svelte_app_path, name="svelte_app"
|
|
741
|
-
)
|
|
742
|
-
self.logger.info(
|
|
743
|
-
f"✅ Svelte dashboard available at /svelte (build: {svelte_build_path})"
|
|
744
|
-
)
|
|
745
|
-
else:
|
|
746
|
-
self.logger.debug(f"Svelte build not found at: {svelte_build_path}")
|
|
747
|
-
|
|
748
877
|
else:
|
|
749
|
-
self.logger.warning(
|
|
878
|
+
self.logger.warning(
|
|
879
|
+
"⚠️ Svelte dashboard not found, serving fallback response"
|
|
880
|
+
)
|
|
750
881
|
|
|
751
882
|
# Fallback handler
|
|
752
883
|
async def fallback_handler(request):
|
|
@@ -773,10 +904,10 @@ class SocketIOServerCore:
|
|
|
773
904
|
self.app.router.add_get("/", error_handler)
|
|
774
905
|
|
|
775
906
|
def _find_static_path(self):
|
|
776
|
-
"""Find the
|
|
907
|
+
"""Find the Svelte build directory using multiple approaches.
|
|
777
908
|
|
|
778
|
-
WHY: The
|
|
779
|
-
|
|
909
|
+
WHY: The dashboard is now pure Svelte, located at dashboard/static/svelte-build/.
|
|
910
|
+
We search for this specific structure across different deployment contexts.
|
|
780
911
|
"""
|
|
781
912
|
# Get deployment-context-aware paths
|
|
782
913
|
try:
|
|
@@ -797,43 +928,45 @@ class SocketIOServerCore:
|
|
|
797
928
|
package_root = None
|
|
798
929
|
project_root = get_project_root()
|
|
799
930
|
|
|
800
|
-
# Try multiple possible locations for
|
|
931
|
+
# Try multiple possible locations for Svelte build directory
|
|
801
932
|
possible_paths = [
|
|
802
933
|
# Package-based paths (for pipx and pip installations)
|
|
803
|
-
package_root / "dashboard" / "
|
|
804
|
-
|
|
805
|
-
|
|
934
|
+
package_root / "dashboard" / "static" / "svelte-build"
|
|
935
|
+
if package_root
|
|
936
|
+
else None,
|
|
806
937
|
# Project-based paths (for development)
|
|
807
|
-
project_root
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
938
|
+
project_root
|
|
939
|
+
/ "src"
|
|
940
|
+
/ "claude_mpm"
|
|
941
|
+
/ "dashboard"
|
|
942
|
+
/ "static"
|
|
943
|
+
/ "svelte-build",
|
|
944
|
+
project_root / "dashboard" / "static" / "svelte-build",
|
|
813
945
|
# Package installation locations (fallback)
|
|
814
|
-
Path(__file__).parent.parent
|
|
815
|
-
|
|
946
|
+
Path(__file__).parent.parent.parent
|
|
947
|
+
/ "dashboard"
|
|
948
|
+
/ "static"
|
|
949
|
+
/ "svelte-build",
|
|
816
950
|
# Scripts directory (for standalone installations)
|
|
817
|
-
get_scripts_dir() / "static",
|
|
818
|
-
get_scripts_dir() / "socketio" / "static",
|
|
951
|
+
get_scripts_dir() / "dashboard" / "static" / "svelte-build",
|
|
819
952
|
# Current working directory
|
|
820
|
-
Path.cwd() / "static",
|
|
821
|
-
Path.cwd() / "
|
|
953
|
+
Path.cwd() / "src" / "claude_mpm" / "dashboard" / "static" / "svelte-build",
|
|
954
|
+
Path.cwd() / "dashboard" / "static" / "svelte-build",
|
|
822
955
|
]
|
|
823
956
|
|
|
824
957
|
# Filter out None values
|
|
825
958
|
possible_paths = [p for p in possible_paths if p is not None]
|
|
826
959
|
self.logger.debug(
|
|
827
|
-
f"Searching {len(possible_paths)} possible
|
|
960
|
+
f"Searching {len(possible_paths)} possible Svelte build locations"
|
|
828
961
|
)
|
|
829
962
|
|
|
830
963
|
for path in possible_paths:
|
|
831
|
-
self.logger.debug(f"Checking for
|
|
964
|
+
self.logger.debug(f"Checking for Svelte build at: {path}")
|
|
832
965
|
try:
|
|
833
966
|
if path.exists() and path.is_dir():
|
|
834
|
-
# Check if it contains expected files
|
|
967
|
+
# Check if it contains expected Svelte build files
|
|
835
968
|
if (path / "index.html").exists():
|
|
836
|
-
self.logger.info(f"✅ Found
|
|
969
|
+
self.logger.info(f"✅ Found Svelte build at: {path}")
|
|
837
970
|
return path
|
|
838
971
|
self.logger.debug(f"Directory exists but no index.html: {path}")
|
|
839
972
|
else:
|
|
@@ -842,7 +975,7 @@ class SocketIOServerCore:
|
|
|
842
975
|
self.logger.debug(f"Error checking path {path}: {e}")
|
|
843
976
|
|
|
844
977
|
self.logger.warning(
|
|
845
|
-
"⚠️
|
|
978
|
+
"⚠️ Svelte build not found - dashboard will not be available"
|
|
846
979
|
)
|
|
847
980
|
self.logger.debug(f"Searched paths: {[str(p) for p in possible_paths]}")
|
|
848
981
|
return None
|
|
@@ -918,9 +1051,15 @@ class SocketIOServerCore:
|
|
|
918
1051
|
},
|
|
919
1052
|
}
|
|
920
1053
|
|
|
921
|
-
# Add to event history
|
|
1054
|
+
# Add to event history UNCONDITIONALLY
|
|
1055
|
+
# WHY: Heartbeat events should persist for new clients too
|
|
922
1056
|
if self.main_server and hasattr(self.main_server, "event_history"):
|
|
923
1057
|
self.main_server.event_history.append(heartbeat_data)
|
|
1058
|
+
self.logger.debug(
|
|
1059
|
+
f"Heartbeat added to history (total: {len(self.main_server.event_history)})"
|
|
1060
|
+
)
|
|
1061
|
+
else:
|
|
1062
|
+
self.logger.warning("event_history not initialized for heartbeat!")
|
|
924
1063
|
|
|
925
1064
|
# Emit heartbeat to all connected clients (already using new schema)
|
|
926
1065
|
await self.sio.emit("system_event", heartbeat_data)
|
|
@@ -658,12 +658,24 @@ class AgentDependencyLoader:
|
|
|
658
658
|
"Robust installer not available, falling back to simple installation"
|
|
659
659
|
)
|
|
660
660
|
try:
|
|
661
|
-
cmd = [sys.executable, "-m", "pip", "install"]
|
|
662
|
-
|
|
663
661
|
# Check environment and add appropriate flags
|
|
664
662
|
import os
|
|
665
663
|
import sysconfig
|
|
666
664
|
|
|
665
|
+
# Check if in UV tool environment (no pip available)
|
|
666
|
+
uv_tool_dir = os.environ.get("UV_TOOL_DIR", "")
|
|
667
|
+
is_uv_tool = (
|
|
668
|
+
(uv_tool_dir and "claude-mpm" in uv_tool_dir)
|
|
669
|
+
or ".local/share/uv/tools/" in sys.executable
|
|
670
|
+
or "/uv/tools/" in sys.executable
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
if is_uv_tool:
|
|
674
|
+
cmd = ["uv", "pip", "install"]
|
|
675
|
+
logger.debug("Using 'uv pip install' for UV tool environment")
|
|
676
|
+
else:
|
|
677
|
+
cmd = [sys.executable, "-m", "pip", "install"]
|
|
678
|
+
|
|
667
679
|
# Check if in virtualenv
|
|
668
680
|
in_virtualenv = (
|
|
669
681
|
(hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix)
|
|
@@ -5,7 +5,7 @@ WHY: This module provides centralized filtering logic to remove non-deployable
|
|
|
5
5
|
agents (BASE_AGENT) and already-deployed agents from user-facing displays.
|
|
6
6
|
|
|
7
7
|
ARCHITECTURE:
|
|
8
|
-
- SOURCE: ~/.claude-mpm/cache/
|
|
8
|
+
- SOURCE: ~/.claude-mpm/cache/agents/ (git repository cache)
|
|
9
9
|
- DEPLOYMENT: .claude/agents/ (project-level deployment location)
|
|
10
10
|
|
|
11
11
|
DESIGN DECISIONS:
|
claude_mpm/utils/migration.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
WHY: Phase 3 of 1M-486 requires migrating from old single-tier deployment
|
|
4
4
|
(~/.claude/agents/, ~/.claude/skills/) to new two-phase architecture:
|
|
5
|
-
- Cache: ~/.claude-mpm/cache/
|
|
5
|
+
- Cache: ~/.claude-mpm/cache/agents/, ~/.claude-mpm/cache/skills/
|
|
6
6
|
- Deployment: .claude-mpm/agents/, .claude-mpm/skills/
|
|
7
7
|
|
|
8
8
|
DESIGN DECISIONS:
|
|
@@ -41,7 +41,7 @@ class MigrationUtility:
|
|
|
41
41
|
self.old_agent_dir = Path.home() / ".claude" / "agents"
|
|
42
42
|
self.old_skill_dir = Path.home() / ".claude" / "skills"
|
|
43
43
|
|
|
44
|
-
self.new_agent_cache = Path.home() / ".claude-mpm" / "cache" / "
|
|
44
|
+
self.new_agent_cache = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
45
45
|
self.new_skill_cache = Path.home() / ".claude-mpm" / "cache" / "skills"
|
|
46
46
|
|
|
47
47
|
def detect_old_locations(self) -> Dict[str, bool]:
|
|
@@ -80,7 +80,7 @@ class MigrationUtility:
|
|
|
80
80
|
def migrate_agents(
|
|
81
81
|
self, dry_run: bool = False, auto_confirm: bool = False
|
|
82
82
|
) -> Dict[str, any]:
|
|
83
|
-
"""Migrate agents from ~/.claude/agents/ to ~/.claude-mpm/cache/
|
|
83
|
+
"""Migrate agents from ~/.claude/agents/ to ~/.claude-mpm/cache/agents/.
|
|
84
84
|
|
|
85
85
|
Args:
|
|
86
86
|
dry_run: Preview migration without making changes
|
|
@@ -329,7 +329,7 @@ class MigrationUtility:
|
|
|
329
329
|
|
|
330
330
|
warning += "\nThe deployment architecture has changed:\n"
|
|
331
331
|
warning += " OLD: ~/.claude/agents/ (single-tier, global)\n"
|
|
332
|
-
warning += " NEW: ~/.claude-mpm/cache/
|
|
332
|
+
warning += " NEW: ~/.claude-mpm/cache/agents/ → .claude-mpm/agents/ (two-phase, per-project)\n\n"
|
|
333
333
|
|
|
334
334
|
warning += "To migrate:\n"
|
|
335
335
|
warning += " claude-mpm migrate\n\n"
|