claude-mpm 4.2.44__py3-none-any.whl → 4.3.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_PM.md +77 -405
- claude_mpm/agents/{INSTRUCTIONS.md → INSTRUCTIONS_OLD_DEPRECATED.md} +75 -1
- claude_mpm/agents/OUTPUT_STYLE.md +0 -39
- claude_mpm/agents/PM_INSTRUCTIONS.md +122 -0
- claude_mpm/agents/WORKFLOW.md +74 -323
- claude_mpm/agents/frontmatter_validator.py +20 -12
- claude_mpm/agents/templates/nextjs_engineer.json +277 -0
- claude_mpm/agents/templates/prompt-engineer.json +294 -0
- claude_mpm/agents/templates/python_engineer.json +289 -0
- claude_mpm/agents/templates/react_engineer.json +11 -3
- claude_mpm/agents/templates/security.json +50 -9
- claude_mpm/cli/commands/agents.py +2 -2
- claude_mpm/cli/commands/uninstall.py +1 -3
- claude_mpm/cli/interactive/agent_wizard.py +3 -3
- claude_mpm/cli/parsers/agent_manager_parser.py +3 -3
- claude_mpm/cli/parsers/agents_parser.py +1 -1
- claude_mpm/constants.py +1 -1
- claude_mpm/core/error_handler.py +2 -4
- claude_mpm/core/file_utils.py +4 -12
- claude_mpm/core/framework_loader.py +72 -24
- claude_mpm/core/log_manager.py +60 -5
- claude_mpm/core/logger.py +1 -1
- claude_mpm/core/logging_utils.py +36 -18
- claude_mpm/core/unified_agent_registry.py +18 -4
- claude_mpm/dashboard/react/components/DataInspector/DataInspector.module.css +188 -0
- claude_mpm/dashboard/react/components/EventViewer/EventViewer.module.css +156 -0
- claude_mpm/dashboard/react/components/shared/ConnectionStatus.module.css +38 -0
- claude_mpm/dashboard/react/components/shared/FilterBar.module.css +92 -0
- claude_mpm/dashboard/static/archive/activity_dashboard_fixed.html +248 -0
- claude_mpm/dashboard/static/archive/activity_dashboard_test.html +61 -0
- claude_mpm/dashboard/static/archive/test_activity_connection.html +179 -0
- claude_mpm/dashboard/static/archive/test_claude_tree_tab.html +68 -0
- claude_mpm/dashboard/static/archive/test_dashboard.html +409 -0
- claude_mpm/dashboard/static/archive/test_dashboard_fixed.html +519 -0
- claude_mpm/dashboard/static/archive/test_dashboard_verification.html +181 -0
- claude_mpm/dashboard/static/archive/test_file_data.html +315 -0
- claude_mpm/dashboard/static/archive/test_file_tree_empty_state.html +243 -0
- claude_mpm/dashboard/static/archive/test_file_tree_fix.html +234 -0
- claude_mpm/dashboard/static/archive/test_file_tree_rename.html +117 -0
- claude_mpm/dashboard/static/archive/test_file_tree_tab.html +115 -0
- claude_mpm/dashboard/static/archive/test_file_viewer.html +224 -0
- claude_mpm/dashboard/static/archive/test_final_activity.html +220 -0
- claude_mpm/dashboard/static/archive/test_tab_fix.html +139 -0
- claude_mpm/dashboard/static/built/assets/events.DjpNxWNo.css +1 -0
- claude_mpm/dashboard/static/built/components/activity-tree.js +1 -1
- claude_mpm/dashboard/static/built/components/agent-hierarchy.js +777 -0
- claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
- claude_mpm/dashboard/static/built/components/build-tracker.js +333 -0
- claude_mpm/dashboard/static/built/components/code-simple.js +857 -0
- claude_mpm/dashboard/static/built/components/code-tree/tree-breadcrumb.js +353 -0
- claude_mpm/dashboard/static/built/components/code-tree/tree-constants.js +235 -0
- claude_mpm/dashboard/static/built/components/code-tree/tree-search.js +409 -0
- claude_mpm/dashboard/static/built/components/code-tree/tree-utils.js +435 -0
- claude_mpm/dashboard/static/built/components/code-viewer.js +2 -1076
- claude_mpm/dashboard/static/built/components/connection-debug.js +654 -0
- claude_mpm/dashboard/static/built/components/diff-viewer.js +891 -0
- claude_mpm/dashboard/static/built/components/event-processor.js +1 -1
- claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/built/components/export-manager.js +1 -1
- claude_mpm/dashboard/static/built/components/file-change-tracker.js +443 -0
- claude_mpm/dashboard/static/built/components/file-change-viewer.js +690 -0
- claude_mpm/dashboard/static/built/components/file-tool-tracker.js +1 -1
- claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/built/components/nav-bar.js +145 -0
- claude_mpm/dashboard/static/built/components/page-structure.js +429 -0
- claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
- claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -465
- claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
- claude_mpm/dashboard/static/built/connection-manager.js +536 -0
- claude_mpm/dashboard/static/built/dashboard.js +1 -1
- claude_mpm/dashboard/static/built/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/built/react/events.js +30 -0
- claude_mpm/dashboard/static/built/shared/dom-helpers.js +396 -0
- claude_mpm/dashboard/static/built/shared/event-bus.js +330 -0
- claude_mpm/dashboard/static/built/shared/event-filter-service.js +540 -0
- claude_mpm/dashboard/static/built/shared/logger.js +385 -0
- claude_mpm/dashboard/static/built/shared/page-structure.js +251 -0
- claude_mpm/dashboard/static/built/shared/tooltip-service.js +253 -0
- claude_mpm/dashboard/static/built/socket-client.js +1 -1
- claude_mpm/dashboard/static/built/tab-isolation-fix.js +185 -0
- claude_mpm/dashboard/static/css/dashboard.css +28 -5
- claude_mpm/dashboard/static/dist/assets/events.DjpNxWNo.css +1 -0
- claude_mpm/dashboard/static/dist/components/activity-tree.js +1 -1
- claude_mpm/dashboard/static/dist/components/agent-inference.js +1 -1
- claude_mpm/dashboard/static/dist/components/code-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +1 -1
- claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/export-manager.js +1 -1
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +1 -1
- claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
- claude_mpm/dashboard/static/dist/components/working-directory.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/react/events.js +30 -0
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/events.html +607 -0
- claude_mpm/dashboard/static/index.html +713 -0
- claude_mpm/dashboard/static/js/components/activity-tree.js +3 -17
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +4 -1
- claude_mpm/dashboard/static/js/components/agent-inference.js +3 -0
- claude_mpm/dashboard/static/js/components/build-tracker.js +8 -0
- claude_mpm/dashboard/static/js/components/code-viewer.js +306 -66
- claude_mpm/dashboard/static/js/components/event-processor.js +3 -0
- claude_mpm/dashboard/static/js/components/event-viewer.js +39 -2
- claude_mpm/dashboard/static/js/components/export-manager.js +3 -0
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +30 -10
- claude_mpm/dashboard/static/js/components/socket-manager.js +4 -0
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +285 -85
- claude_mpm/dashboard/static/js/components/working-directory.js +3 -0
- claude_mpm/dashboard/static/js/dashboard.js +61 -33
- claude_mpm/dashboard/static/js/socket-client.js +12 -8
- claude_mpm/dashboard/static/js/stores/dashboard-store.js +562 -0
- claude_mpm/dashboard/static/js/tab-isolation-fix.js +185 -0
- claude_mpm/dashboard/static/legacy/activity.html +736 -0
- claude_mpm/dashboard/static/legacy/agents.html +786 -0
- claude_mpm/dashboard/static/legacy/files.html +747 -0
- claude_mpm/dashboard/static/legacy/tools.html +831 -0
- claude_mpm/dashboard/static/monitors-index.html +218 -0
- claude_mpm/dashboard/static/monitors.html +431 -0
- claude_mpm/dashboard/static/production/events.html +659 -0
- claude_mpm/dashboard/static/production/main.html +715 -0
- claude_mpm/dashboard/static/production/monitors.html +483 -0
- claude_mpm/dashboard/static/socket.io.min.js +7 -0
- claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +7 -0
- claude_mpm/dashboard/static/test-archive/dashboard.html +635 -0
- claude_mpm/dashboard/static/test-archive/debug-events.html +147 -0
- claude_mpm/dashboard/static/test-archive/test-navigation.html +256 -0
- claude_mpm/dashboard/static/test-archive/test-react-exports.html +180 -0
- claude_mpm/dashboard/templates/index.html +79 -9
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +1 -1
- claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +285 -26
- claude_mpm/services/agents/deployment/agent_validator.py +3 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +13 -4
- claude_mpm/services/agents/local_template_manager.py +2 -7
- claude_mpm/services/monitor/daemon.py +1 -2
- claude_mpm/services/monitor/daemon_manager.py +2 -7
- claude_mpm/services/monitor/event_emitter.py +6 -2
- claude_mpm/services/monitor/handlers/code_analysis.py +4 -6
- claude_mpm/services/monitor/handlers/hooks.py +2 -6
- claude_mpm/services/monitor/server.py +27 -4
- claude_mpm/tools/code_tree_analyzer.py +2 -4
- claude_mpm/utils/log_cleanup.py +612 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.3.0.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.3.0.dist-info}/RECORD +151 -83
- claude_mpm/dashboard/static/test-browser-monitor.html +0 -470
- claude_mpm/dashboard/static/test-simple.html +0 -97
- /claude_mpm/dashboard/static/{test_debug.html → test-archive/test_debug.html} +0 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.3.0.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.3.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.3.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.3.0.dist-info}/top_level.txt +0 -0
claude_mpm/constants.py
CHANGED
|
@@ -229,7 +229,7 @@ class Paths(str, Enum):
|
|
|
229
229
|
|
|
230
230
|
CLAUDE_AGENTS_DIR = ".claude/agents"
|
|
231
231
|
CLAUDE_CONFIG_DIR = ".claude"
|
|
232
|
-
MPM_LOG_DIR = "logs/mpm"
|
|
232
|
+
MPM_LOG_DIR = ".claude-mpm/logs/mpm"
|
|
233
233
|
MPM_SESSION_DIR = ".claude-mpm/session"
|
|
234
234
|
MPM_PROMPTS_DIR = ".claude-mpm/prompts"
|
|
235
235
|
|
claude_mpm/core/error_handler.py
CHANGED
|
@@ -145,6 +145,7 @@ class ErrorHandler:
|
|
|
145
145
|
if strategy == ErrorStrategy.TERMINATE:
|
|
146
146
|
self.logger.critical(f"Terminating due to critical error: {error}")
|
|
147
147
|
sys.exit(1)
|
|
148
|
+
return None
|
|
148
149
|
|
|
149
150
|
def _log_error(
|
|
150
151
|
self,
|
|
@@ -307,10 +308,7 @@ def handle_error(
|
|
|
307
308
|
Returns:
|
|
308
309
|
Result based on strategy
|
|
309
310
|
"""
|
|
310
|
-
if logger
|
|
311
|
-
handler = ErrorHandler(logger=logger)
|
|
312
|
-
else:
|
|
313
|
-
handler = _global_handler
|
|
311
|
+
handler = ErrorHandler(logger=logger) if logger else _global_handler
|
|
314
312
|
|
|
315
313
|
return handler.handle(
|
|
316
314
|
error=error,
|
claude_mpm/core/file_utils.py
CHANGED
|
@@ -11,7 +11,7 @@ import json
|
|
|
11
11
|
import os
|
|
12
12
|
import shutil
|
|
13
13
|
import tempfile
|
|
14
|
-
from contextlib import contextmanager
|
|
14
|
+
from contextlib import contextmanager, suppress
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
from typing import Any, List, Optional, Union
|
|
17
17
|
|
|
@@ -374,10 +374,8 @@ def atomic_write(
|
|
|
374
374
|
except OSError as e:
|
|
375
375
|
logger.error(f"Error in atomic write to {filepath}: {e}")
|
|
376
376
|
# Clean up temporary file
|
|
377
|
-
|
|
377
|
+
with suppress(Exception):
|
|
378
378
|
os.unlink(temp_path)
|
|
379
|
-
except:
|
|
380
|
-
pass
|
|
381
379
|
return False
|
|
382
380
|
|
|
383
381
|
|
|
@@ -703,10 +701,7 @@ def validate_file(
|
|
|
703
701
|
return False
|
|
704
702
|
|
|
705
703
|
# Check extension
|
|
706
|
-
|
|
707
|
-
return False
|
|
708
|
-
|
|
709
|
-
return True
|
|
704
|
+
return not (extensions and filepath.suffix not in extensions)
|
|
710
705
|
|
|
711
706
|
|
|
712
707
|
def get_file_hash(
|
|
@@ -760,10 +755,7 @@ def find_files(
|
|
|
760
755
|
if not directory.exists():
|
|
761
756
|
return []
|
|
762
757
|
|
|
763
|
-
if recursive
|
|
764
|
-
paths = directory.rglob(pattern)
|
|
765
|
-
else:
|
|
766
|
-
paths = directory.glob(pattern)
|
|
758
|
+
paths = directory.rglob(pattern) if recursive else directory.glob(pattern)
|
|
767
759
|
|
|
768
760
|
if file_only:
|
|
769
761
|
return [p for p in paths if p.is_file()]
|
|
@@ -639,7 +639,14 @@ class FrameworkLoader:
|
|
|
639
639
|
self._load_packaged_framework_content(content)
|
|
640
640
|
else:
|
|
641
641
|
# Load from filesystem for development mode
|
|
642
|
-
#
|
|
642
|
+
# Try new consolidated PM_INSTRUCTIONS.md first, fall back to INSTRUCTIONS.md
|
|
643
|
+
pm_instructions_path = (
|
|
644
|
+
self.framework_path
|
|
645
|
+
/ "src"
|
|
646
|
+
/ "claude_mpm"
|
|
647
|
+
/ "agents"
|
|
648
|
+
/ "PM_INSTRUCTIONS.md"
|
|
649
|
+
)
|
|
643
650
|
framework_instructions_path = (
|
|
644
651
|
self.framework_path
|
|
645
652
|
/ "src"
|
|
@@ -647,12 +654,25 @@ class FrameworkLoader:
|
|
|
647
654
|
/ "agents"
|
|
648
655
|
/ "INSTRUCTIONS.md"
|
|
649
656
|
)
|
|
650
|
-
|
|
657
|
+
|
|
658
|
+
# Try loading new consolidated file first
|
|
659
|
+
if pm_instructions_path.exists():
|
|
651
660
|
loaded_content = self._try_load_file(
|
|
652
|
-
|
|
661
|
+
pm_instructions_path, "consolidated PM_INSTRUCTIONS.md"
|
|
653
662
|
)
|
|
654
663
|
if loaded_content:
|
|
655
664
|
content["framework_instructions"] = loaded_content
|
|
665
|
+
self.logger.info("Loaded consolidated PM_INSTRUCTIONS.md")
|
|
666
|
+
# Fall back to legacy file for backward compatibility
|
|
667
|
+
elif framework_instructions_path.exists():
|
|
668
|
+
loaded_content = self._try_load_file(
|
|
669
|
+
framework_instructions_path, "framework INSTRUCTIONS.md (legacy)"
|
|
670
|
+
)
|
|
671
|
+
if loaded_content:
|
|
672
|
+
content["framework_instructions"] = loaded_content
|
|
673
|
+
self.logger.warning(
|
|
674
|
+
"Using legacy INSTRUCTIONS.md - consider migrating to PM_INSTRUCTIONS.md"
|
|
675
|
+
)
|
|
656
676
|
content["loaded"] = True
|
|
657
677
|
# Add framework version to content
|
|
658
678
|
if self.framework_version:
|
|
@@ -717,20 +737,33 @@ class FrameworkLoader:
|
|
|
717
737
|
return
|
|
718
738
|
|
|
719
739
|
try:
|
|
720
|
-
#
|
|
721
|
-
|
|
722
|
-
if
|
|
723
|
-
content["framework_instructions"] =
|
|
740
|
+
# Try new consolidated PM_INSTRUCTIONS.md first
|
|
741
|
+
pm_instructions_content = self._load_packaged_file("PM_INSTRUCTIONS.md")
|
|
742
|
+
if pm_instructions_content:
|
|
743
|
+
content["framework_instructions"] = pm_instructions_content
|
|
724
744
|
content["loaded"] = True
|
|
745
|
+
self.logger.info("Loaded consolidated PM_INSTRUCTIONS.md from package")
|
|
725
746
|
# Extract and store version/timestamp metadata
|
|
726
747
|
self._extract_metadata_from_content(
|
|
727
|
-
|
|
748
|
+
pm_instructions_content, "PM_INSTRUCTIONS.md"
|
|
728
749
|
)
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
if
|
|
733
|
-
content["
|
|
750
|
+
else:
|
|
751
|
+
# Fall back to legacy INSTRUCTIONS.md
|
|
752
|
+
instructions_content = self._load_packaged_file("INSTRUCTIONS.md")
|
|
753
|
+
if instructions_content:
|
|
754
|
+
content["framework_instructions"] = instructions_content
|
|
755
|
+
content["loaded"] = True
|
|
756
|
+
self.logger.warning("Using legacy INSTRUCTIONS.md from package")
|
|
757
|
+
# Extract and store version/timestamp metadata
|
|
758
|
+
self._extract_metadata_from_content(
|
|
759
|
+
instructions_content, "INSTRUCTIONS.md"
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
if self.framework_version:
|
|
763
|
+
content["instructions_version"] = self.framework_version
|
|
764
|
+
content["version"] = self.framework_version
|
|
765
|
+
if self.framework_last_modified:
|
|
766
|
+
content["instructions_last_modified"] = self.framework_last_modified
|
|
734
767
|
|
|
735
768
|
# Load BASE_PM.md
|
|
736
769
|
base_pm_content = self._load_packaged_file("BASE_PM.md")
|
|
@@ -757,22 +790,37 @@ class FrameworkLoader:
|
|
|
757
790
|
) -> None:
|
|
758
791
|
"""Load framework content using importlib.resources fallback."""
|
|
759
792
|
try:
|
|
760
|
-
#
|
|
761
|
-
|
|
762
|
-
"
|
|
793
|
+
# Try new consolidated PM_INSTRUCTIONS.md first
|
|
794
|
+
pm_instructions_content = self._load_packaged_file_fallback(
|
|
795
|
+
"PM_INSTRUCTIONS.md", resources
|
|
763
796
|
)
|
|
764
|
-
if
|
|
765
|
-
content["framework_instructions"] =
|
|
797
|
+
if pm_instructions_content:
|
|
798
|
+
content["framework_instructions"] = pm_instructions_content
|
|
766
799
|
content["loaded"] = True
|
|
800
|
+
self.logger.info("Loaded consolidated PM_INSTRUCTIONS.md via fallback")
|
|
767
801
|
# Extract and store version/timestamp metadata
|
|
768
802
|
self._extract_metadata_from_content(
|
|
769
|
-
|
|
803
|
+
pm_instructions_content, "PM_INSTRUCTIONS.md"
|
|
770
804
|
)
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
805
|
+
else:
|
|
806
|
+
# Fall back to legacy INSTRUCTIONS.md
|
|
807
|
+
instructions_content = self._load_packaged_file_fallback(
|
|
808
|
+
"INSTRUCTIONS.md", resources
|
|
809
|
+
)
|
|
810
|
+
if instructions_content:
|
|
811
|
+
content["framework_instructions"] = instructions_content
|
|
812
|
+
content["loaded"] = True
|
|
813
|
+
self.logger.warning("Using legacy INSTRUCTIONS.md via fallback")
|
|
814
|
+
# Extract and store version/timestamp metadata
|
|
815
|
+
self._extract_metadata_from_content(
|
|
816
|
+
instructions_content, "INSTRUCTIONS.md"
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
if self.framework_version:
|
|
820
|
+
content["instructions_version"] = self.framework_version
|
|
821
|
+
content["version"] = self.framework_version
|
|
822
|
+
if self.framework_last_modified:
|
|
823
|
+
content["instructions_last_modified"] = self.framework_last_modified
|
|
776
824
|
|
|
777
825
|
# Load BASE_PM.md
|
|
778
826
|
base_pm_content = self._load_packaged_file_fallback("BASE_PM.md", resources)
|
claude_mpm/core/log_manager.py
CHANGED
|
@@ -29,6 +29,12 @@ from ..core.constants import SystemLimits
|
|
|
29
29
|
|
|
30
30
|
logger = logging.getLogger(__name__)
|
|
31
31
|
|
|
32
|
+
# Import cleanup utility for automatic cleanup
|
|
33
|
+
try:
|
|
34
|
+
from ..utils.log_cleanup import run_cleanup_on_startup
|
|
35
|
+
except ImportError:
|
|
36
|
+
run_cleanup_on_startup = None
|
|
37
|
+
|
|
32
38
|
|
|
33
39
|
class LogManager:
|
|
34
40
|
"""
|
|
@@ -76,6 +82,9 @@ class LogManager:
|
|
|
76
82
|
# Start background threads
|
|
77
83
|
self._start_background_threads()
|
|
78
84
|
|
|
85
|
+
# Run automatic cleanup on startup if enabled
|
|
86
|
+
self._run_startup_cleanup()
|
|
87
|
+
|
|
79
88
|
def _setup_logging_config(self):
|
|
80
89
|
"""Load and setup logging configuration from config."""
|
|
81
90
|
logging_config = self.config.get("logging", {})
|
|
@@ -101,10 +110,55 @@ class LogManager:
|
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
# Base directories
|
|
104
|
-
self.base_log_dir = Path(
|
|
113
|
+
self.base_log_dir = Path(
|
|
114
|
+
logging_config.get("base_directory", ".claude-mpm/logs")
|
|
115
|
+
)
|
|
105
116
|
if not self.base_log_dir.is_absolute():
|
|
106
117
|
self.base_log_dir = Path.cwd() / self.base_log_dir
|
|
107
118
|
|
|
119
|
+
def _run_startup_cleanup(self):
|
|
120
|
+
"""Run automatic log cleanup on startup if enabled."""
|
|
121
|
+
if run_cleanup_on_startup is None:
|
|
122
|
+
return # Cleanup utility not available
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
# Get cleanup configuration
|
|
126
|
+
cleanup_config = self.config.get("log_cleanup", {})
|
|
127
|
+
|
|
128
|
+
# Check if automatic cleanup is enabled (default: True)
|
|
129
|
+
if not cleanup_config.get("auto_cleanup_enabled", True):
|
|
130
|
+
logger.debug("Automatic log cleanup is disabled")
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
# Convert hours to days for cleanup utility
|
|
134
|
+
cleanup_params = {
|
|
135
|
+
"auto_cleanup_enabled": True,
|
|
136
|
+
"session_retention_days": self.retention_hours.get("sessions", 168)
|
|
137
|
+
// 24,
|
|
138
|
+
"archive_retention_days": cleanup_config.get(
|
|
139
|
+
"archive_retention_days", 30
|
|
140
|
+
),
|
|
141
|
+
"log_retention_days": cleanup_config.get("log_retention_days", 14),
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Run cleanup in background thread to avoid blocking startup
|
|
145
|
+
def cleanup_task():
|
|
146
|
+
try:
|
|
147
|
+
result = run_cleanup_on_startup(self.base_log_dir, cleanup_params)
|
|
148
|
+
if result:
|
|
149
|
+
logger.debug(
|
|
150
|
+
f"Startup cleanup completed: "
|
|
151
|
+
f"Removed {result.get('total_removed', 0)} items"
|
|
152
|
+
)
|
|
153
|
+
except Exception as e:
|
|
154
|
+
logger.debug(f"Startup cleanup failed: {e}")
|
|
155
|
+
|
|
156
|
+
cleanup_thread = Thread(target=cleanup_task, daemon=True)
|
|
157
|
+
cleanup_thread.start()
|
|
158
|
+
|
|
159
|
+
except Exception as e:
|
|
160
|
+
logger.debug(f"Could not run startup cleanup: {e}")
|
|
161
|
+
|
|
108
162
|
def _start_background_threads(self):
|
|
109
163
|
"""Start background threads for async operations."""
|
|
110
164
|
with self._lock:
|
|
@@ -350,13 +404,14 @@ class LogManager:
|
|
|
350
404
|
"""
|
|
351
405
|
One-time migration to move existing MPM logs to new subdirectory.
|
|
352
406
|
|
|
353
|
-
Moves mpm_*.log files from .claude-mpm/logs/
|
|
407
|
+
Moves mpm_*.log files from old locations to .claude-mpm/logs/mpm/
|
|
354
408
|
"""
|
|
355
409
|
try:
|
|
356
|
-
# Check
|
|
410
|
+
# Check old possible locations (including incorrectly created ones)
|
|
357
411
|
old_locations = [
|
|
358
|
-
Path.cwd() / "
|
|
359
|
-
|
|
412
|
+
Path.cwd() / "logs", # Incorrectly created in project root
|
|
413
|
+
Path.cwd() / ".claude-mpm" / "logs", # Correct base location
|
|
414
|
+
self.base_log_dir, # Current base location (.claude-mpm/logs/)
|
|
360
415
|
]
|
|
361
416
|
new_location = self.base_log_dir / "mpm"
|
|
362
417
|
|
claude_mpm/core/logger.py
CHANGED
|
@@ -232,7 +232,7 @@ def setup_logging(
|
|
|
232
232
|
if log_dir is None:
|
|
233
233
|
# Use deployment root for logs to keep everything centralized
|
|
234
234
|
deployment_root = get_project_root()
|
|
235
|
-
log_dir = deployment_root / "logs" / "mpm"
|
|
235
|
+
log_dir = deployment_root / ".claude-mpm" / "logs" / "mpm"
|
|
236
236
|
|
|
237
237
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
238
238
|
|
claude_mpm/core/logging_utils.py
CHANGED
|
@@ -42,8 +42,10 @@ class LoggingConfig:
|
|
|
42
42
|
ISO_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
43
43
|
|
|
44
44
|
# File settings
|
|
45
|
-
MAX_BYTES =
|
|
45
|
+
MAX_BYTES = 5 * 1024 * 1024 # 5MB - lowered for better rotation testing
|
|
46
46
|
BACKUP_COUNT = 5
|
|
47
|
+
ROTATION_INTERVAL = "midnight" # Daily rotation at midnight
|
|
48
|
+
ROTATION_BACKUP_COUNT = 7 # Keep 7 days of daily logs
|
|
47
49
|
|
|
48
50
|
# Component-specific log names
|
|
49
51
|
COMPONENT_NAMES = {
|
|
@@ -76,11 +78,11 @@ class LoggerFactory:
|
|
|
76
78
|
@classmethod
|
|
77
79
|
def initialize(
|
|
78
80
|
cls,
|
|
79
|
-
log_level: str = None,
|
|
81
|
+
log_level: Optional[str] = None,
|
|
80
82
|
log_dir: Optional[Path] = None,
|
|
81
83
|
log_to_file: bool = False,
|
|
82
|
-
log_format: str = None,
|
|
83
|
-
date_format: str = None,
|
|
84
|
+
log_format: Optional[str] = None,
|
|
85
|
+
date_format: Optional[str] = None,
|
|
84
86
|
) -> None:
|
|
85
87
|
"""Initialize the logging system globally.
|
|
86
88
|
|
|
@@ -129,30 +131,46 @@ class LoggerFactory:
|
|
|
129
131
|
log_format: Optional[str] = None,
|
|
130
132
|
date_format: Optional[str] = None,
|
|
131
133
|
) -> None:
|
|
132
|
-
"""Set up file logging
|
|
134
|
+
"""Set up file logging handlers with both size and time-based rotation."""
|
|
133
135
|
if not cls._log_dir:
|
|
134
136
|
return
|
|
135
137
|
|
|
136
138
|
# Ensure log directory exists
|
|
137
139
|
cls._log_dir.mkdir(parents=True, exist_ok=True)
|
|
138
140
|
|
|
139
|
-
|
|
141
|
+
formatter = logging.Formatter(
|
|
142
|
+
log_format or LoggingConfig.DETAILED_FORMAT,
|
|
143
|
+
date_format or LoggingConfig.DATE_FORMAT,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# 1. Size-based rotating file handler (for current active log)
|
|
140
147
|
log_file = cls._log_dir / "claude_mpm.log"
|
|
141
|
-
|
|
148
|
+
size_handler = logging.handlers.RotatingFileHandler(
|
|
142
149
|
log_file,
|
|
143
150
|
maxBytes=LoggingConfig.MAX_BYTES,
|
|
144
151
|
backupCount=LoggingConfig.BACKUP_COUNT,
|
|
145
152
|
)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
153
|
+
size_handler.setLevel(LoggingConfig.LEVELS.get(cls._log_level, logging.INFO))
|
|
154
|
+
size_handler.setFormatter(formatter)
|
|
155
|
+
logging.getLogger().addHandler(size_handler)
|
|
156
|
+
cls._handlers["file"] = size_handler
|
|
157
|
+
|
|
158
|
+
# 2. Time-based rotating file handler (daily rotation)
|
|
159
|
+
daily_log_file = cls._log_dir / "claude_mpm_daily.log"
|
|
160
|
+
time_handler = logging.handlers.TimedRotatingFileHandler(
|
|
161
|
+
daily_log_file,
|
|
162
|
+
when=LoggingConfig.ROTATION_INTERVAL,
|
|
163
|
+
interval=1,
|
|
164
|
+
backupCount=LoggingConfig.ROTATION_BACKUP_COUNT,
|
|
151
165
|
)
|
|
152
|
-
|
|
166
|
+
time_handler.setLevel(LoggingConfig.LEVELS.get(cls._log_level, logging.INFO))
|
|
167
|
+
time_handler.setFormatter(formatter)
|
|
168
|
+
|
|
169
|
+
# Add suffix to rotated files (e.g., claude_mpm_daily.log.2024-09-18)
|
|
170
|
+
time_handler.suffix = "%Y-%m-%d"
|
|
153
171
|
|
|
154
|
-
logging.getLogger().addHandler(
|
|
155
|
-
cls._handlers["
|
|
172
|
+
logging.getLogger().addHandler(time_handler)
|
|
173
|
+
cls._handlers["file_daily"] = time_handler
|
|
156
174
|
|
|
157
175
|
@classmethod
|
|
158
176
|
def get_logger(
|
|
@@ -268,10 +286,10 @@ def get_logger(
|
|
|
268
286
|
|
|
269
287
|
|
|
270
288
|
def initialize_logging(
|
|
271
|
-
log_level: str = None,
|
|
289
|
+
log_level: Optional[str] = None,
|
|
272
290
|
log_dir: Optional[Path] = None,
|
|
273
291
|
log_to_file: bool = False,
|
|
274
|
-
log_format: str = None,
|
|
292
|
+
log_format: Optional[str] = None,
|
|
275
293
|
) -> None:
|
|
276
294
|
"""Initialize the logging system with standard configuration.
|
|
277
295
|
|
|
@@ -300,7 +318,7 @@ def set_log_level(level: str) -> None:
|
|
|
300
318
|
LoggerFactory.set_level(level)
|
|
301
319
|
|
|
302
320
|
|
|
303
|
-
def get_component_logger(component: str, name: str = None) -> logging.Logger:
|
|
321
|
+
def get_component_logger(component: str, name: Optional[str] = None) -> logging.Logger:
|
|
304
322
|
"""Get a logger for a specific component.
|
|
305
323
|
|
|
306
324
|
Args:
|
|
@@ -367,9 +367,17 @@ class UnifiedAgentRegistry:
|
|
|
367
367
|
break
|
|
368
368
|
project_root = project_root.parent
|
|
369
369
|
|
|
370
|
-
# Extract tags
|
|
370
|
+
# Extract tags (handle both list and comma-separated string formats)
|
|
371
371
|
if "metadata" in data:
|
|
372
|
-
|
|
372
|
+
tags_raw = data["metadata"].get("tags", [])
|
|
373
|
+
if isinstance(tags_raw, str):
|
|
374
|
+
tags = [
|
|
375
|
+
tag.strip()
|
|
376
|
+
for tag in tags_raw.split(",")
|
|
377
|
+
if tag.strip()
|
|
378
|
+
]
|
|
379
|
+
else:
|
|
380
|
+
tags = tags_raw if isinstance(tags_raw, list) else []
|
|
373
381
|
|
|
374
382
|
except Exception as e:
|
|
375
383
|
logger.debug(
|
|
@@ -462,8 +470,14 @@ class UnifiedAgentRegistry:
|
|
|
462
470
|
"specializations", data.get("specializations", [])
|
|
463
471
|
)
|
|
464
472
|
|
|
465
|
-
# Extract tags as specializations if present
|
|
466
|
-
|
|
473
|
+
# Extract tags as specializations if present (handle both formats)
|
|
474
|
+
tags_raw = metadata.get("tags", [])
|
|
475
|
+
if isinstance(tags_raw, str):
|
|
476
|
+
tags = [
|
|
477
|
+
tag.strip() for tag in tags_raw.split(",") if tag.strip()
|
|
478
|
+
]
|
|
479
|
+
else:
|
|
480
|
+
tags = tags_raw if isinstance(tags_raw, list) else []
|
|
467
481
|
if tags and not specializations:
|
|
468
482
|
specializations = tags
|
|
469
483
|
else:
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
.dataInspector {
|
|
2
|
+
background: rgba(0, 0, 0, 0.3);
|
|
3
|
+
border-radius: 6px;
|
|
4
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.inspectorHeader {
|
|
9
|
+
display: flex;
|
|
10
|
+
gap: 10px;
|
|
11
|
+
padding: 10px;
|
|
12
|
+
background: rgba(255, 255, 255, 0.05);
|
|
13
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
14
|
+
align-items: center;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.searchInput {
|
|
18
|
+
flex: 1;
|
|
19
|
+
padding: 8px 12px;
|
|
20
|
+
background: rgba(255, 255, 255, 0.05);
|
|
21
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
22
|
+
border-radius: 4px;
|
|
23
|
+
color: white;
|
|
24
|
+
font-size: 12px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.searchInput::placeholder {
|
|
28
|
+
color: rgba(255, 255, 255, 0.5);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.actions {
|
|
32
|
+
display: flex;
|
|
33
|
+
gap: 8px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.actionButton {
|
|
37
|
+
padding: 6px 12px;
|
|
38
|
+
background: rgba(255, 255, 255, 0.1);
|
|
39
|
+
border: none;
|
|
40
|
+
border-radius: 4px;
|
|
41
|
+
color: white;
|
|
42
|
+
font-size: 11px;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
transition: background 0.2s;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.actionButton:hover {
|
|
48
|
+
background: rgba(255, 255, 255, 0.2);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.treeContainer {
|
|
52
|
+
overflow-y: auto;
|
|
53
|
+
padding: 10px;
|
|
54
|
+
font-family: 'Courier New', monospace;
|
|
55
|
+
font-size: 13px;
|
|
56
|
+
line-height: 1.4;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.treeNode {
|
|
60
|
+
margin-bottom: 2px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.nodeHeader {
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: 6px;
|
|
67
|
+
padding: 2px 4px;
|
|
68
|
+
cursor: pointer;
|
|
69
|
+
border-radius: 3px;
|
|
70
|
+
transition: background 0.2s;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.nodeHeader:hover {
|
|
74
|
+
background: rgba(255, 255, 255, 0.05);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.expandIcon {
|
|
78
|
+
color: #94a3b8;
|
|
79
|
+
font-size: 10px;
|
|
80
|
+
transition: transform 0.2s;
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
user-select: none;
|
|
83
|
+
width: 12px;
|
|
84
|
+
display: inline-block;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.expandIcon.expanded {
|
|
88
|
+
transform: rotate(90deg);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.leafIcon {
|
|
92
|
+
color: #94a3b8;
|
|
93
|
+
font-size: 8px;
|
|
94
|
+
width: 12px;
|
|
95
|
+
display: inline-block;
|
|
96
|
+
text-align: center;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.nodeKey {
|
|
100
|
+
color: #60a5fa;
|
|
101
|
+
font-weight: 500;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.nodeValue {
|
|
105
|
+
margin-left: 6px;
|
|
106
|
+
word-break: break-all;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.nodeValue.string {
|
|
110
|
+
color: #86efac;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.nodeValue.number {
|
|
114
|
+
color: #fbbf24;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.nodeValue.boolean {
|
|
118
|
+
color: #a78bfa;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.nodeValue.null,
|
|
122
|
+
.nodeValue.undefined {
|
|
123
|
+
color: #f87171;
|
|
124
|
+
font-style: italic;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.nodeValue.object,
|
|
128
|
+
.nodeValue.array {
|
|
129
|
+
color: #94a3b8;
|
|
130
|
+
font-style: italic;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.copyButton {
|
|
134
|
+
margin-left: auto;
|
|
135
|
+
background: none;
|
|
136
|
+
border: none;
|
|
137
|
+
color: rgba(255, 255, 255, 0.5);
|
|
138
|
+
cursor: pointer;
|
|
139
|
+
padding: 2px;
|
|
140
|
+
font-size: 10px;
|
|
141
|
+
opacity: 0;
|
|
142
|
+
transition: opacity 0.2s;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.nodeHeader:hover .copyButton {
|
|
146
|
+
opacity: 1;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.copyButton:hover {
|
|
150
|
+
color: rgba(255, 255, 255, 0.8);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.nodeChildren {
|
|
154
|
+
border-left: 1px solid rgba(255, 255, 255, 0.1);
|
|
155
|
+
margin-left: 6px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.highlight {
|
|
159
|
+
background: rgba(251, 191, 36, 0.3);
|
|
160
|
+
color: #fbbf24;
|
|
161
|
+
padding: 1px 2px;
|
|
162
|
+
border-radius: 2px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.noData {
|
|
166
|
+
text-align: center;
|
|
167
|
+
color: rgba(255, 255, 255, 0.5);
|
|
168
|
+
padding: 20px;
|
|
169
|
+
font-style: italic;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* Scrollbar styling */
|
|
173
|
+
.treeContainer::-webkit-scrollbar {
|
|
174
|
+
width: 6px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.treeContainer::-webkit-scrollbar-track {
|
|
178
|
+
background: rgba(255, 255, 255, 0.05);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.treeContainer::-webkit-scrollbar-thumb {
|
|
182
|
+
background: rgba(255, 255, 255, 0.2);
|
|
183
|
+
border-radius: 3px;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.treeContainer::-webkit-scrollbar-thumb:hover {
|
|
187
|
+
background: rgba(255, 255, 255, 0.3);
|
|
188
|
+
}
|