claude-mpm 4.0.28__py3-none-any.whl → 4.0.30__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/agents/BASE_AGENT_TEMPLATE.md +48 -3
- claude_mpm/agents/BASE_PM.md +20 -15
- claude_mpm/agents/INSTRUCTIONS.md +12 -2
- claude_mpm/agents/templates/agent-manager.json +24 -0
- claude_mpm/agents/templates/agent-manager.md +304 -0
- claude_mpm/agents/templates/documentation.json +16 -3
- claude_mpm/agents/templates/engineer.json +19 -5
- claude_mpm/agents/templates/ops.json +19 -5
- claude_mpm/agents/templates/qa.json +16 -3
- claude_mpm/agents/templates/refactoring_engineer.json +25 -7
- claude_mpm/agents/templates/research.json +19 -5
- claude_mpm/cli/__init__.py +4 -0
- claude_mpm/cli/commands/__init__.py +4 -0
- claude_mpm/cli/commands/agent_manager.py +521 -0
- claude_mpm/cli/commands/agents.py +2 -1
- claude_mpm/cli/commands/cleanup.py +1 -1
- claude_mpm/cli/commands/doctor.py +209 -0
- claude_mpm/cli/commands/mcp.py +3 -3
- claude_mpm/cli/commands/mcp_install_commands.py +12 -30
- claude_mpm/cli/commands/mcp_server_commands.py +9 -9
- claude_mpm/cli/commands/memory.py +1 -1
- claude_mpm/cli/commands/run.py +31 -2
- claude_mpm/cli/commands/run_config_checker.py +1 -1
- claude_mpm/cli/parsers/agent_manager_parser.py +247 -0
- claude_mpm/cli/parsers/base_parser.py +12 -1
- claude_mpm/cli/parsers/mcp_parser.py +1 -1
- claude_mpm/cli/parsers/run_parser.py +1 -1
- claude_mpm/cli/shared/__init__.py +1 -1
- claude_mpm/cli/startup_logging.py +463 -0
- claude_mpm/constants.py +2 -0
- claude_mpm/core/claude_runner.py +81 -2
- claude_mpm/core/constants.py +2 -2
- claude_mpm/core/framework_loader.py +45 -11
- claude_mpm/core/interactive_session.py +82 -3
- claude_mpm/core/output_style_manager.py +6 -6
- claude_mpm/core/socketio_pool.py +2 -2
- claude_mpm/core/unified_paths.py +128 -0
- claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/built/dashboard.js +1 -1
- claude_mpm/dashboard/static/built/socket-client.js +1 -1
- claude_mpm/dashboard/static/css/dashboard.css +170 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +21 -3
- claude_mpm/dashboard/static/js/components/module-viewer.js +129 -1
- claude_mpm/dashboard/static/js/dashboard.js +116 -0
- claude_mpm/dashboard/static/js/socket-client.js +0 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +1 -1
- claude_mpm/hooks/claude_hooks/hook_handler.py +1 -1
- claude_mpm/scripts/mcp_server.py +2 -2
- claude_mpm/services/agents/agent_builder.py +455 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +10 -3
- claude_mpm/services/agents/deployment/agent_validator.py +1 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +69 -1
- claude_mpm/services/diagnostics/__init__.py +18 -0
- claude_mpm/services/diagnostics/checks/__init__.py +30 -0
- claude_mpm/services/diagnostics/checks/agent_check.py +319 -0
- claude_mpm/services/diagnostics/checks/base_check.py +64 -0
- claude_mpm/services/diagnostics/checks/claude_desktop_check.py +283 -0
- claude_mpm/services/diagnostics/checks/common_issues_check.py +354 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +300 -0
- claude_mpm/services/diagnostics/checks/filesystem_check.py +233 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +255 -0
- claude_mpm/services/diagnostics/checks/mcp_check.py +315 -0
- claude_mpm/services/diagnostics/checks/monitor_check.py +282 -0
- claude_mpm/services/diagnostics/checks/startup_log_check.py +322 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +247 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +283 -0
- claude_mpm/services/diagnostics/models.py +120 -0
- claude_mpm/services/mcp_gateway/core/interfaces.py +1 -1
- claude_mpm/services/mcp_gateway/main.py +1 -1
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +3 -3
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
- claude_mpm/services/mcp_gateway/server/stdio_server.py +3 -3
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +2 -2
- claude_mpm/services/memory/__init__.py +2 -0
- claude_mpm/services/socketio/handlers/connection.py +27 -33
- claude_mpm/services/socketio/handlers/registry.py +39 -7
- claude_mpm/services/socketio/server/core.py +72 -22
- claude_mpm/validation/frontmatter_validator.py +1 -1
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.30.dist-info}/METADATA +4 -1
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.30.dist-info}/RECORD +89 -67
- /claude_mpm/cli/shared/{command_base.py → base_command.py} +0 -0
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.30.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.30.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.30.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.28.dist-info → claude_mpm-4.0.30.dist-info}/top_level.txt +0 -0
|
@@ -77,11 +77,11 @@ class FrameworkLoader:
|
|
|
77
77
|
output_style_content = self.output_style_manager.extract_output_style_content(framework_loader=self)
|
|
78
78
|
output_style_path = self.output_style_manager.save_output_style(output_style_content)
|
|
79
79
|
|
|
80
|
-
# Deploy to Claude
|
|
80
|
+
# Deploy to Claude Code if supported
|
|
81
81
|
deployed = self.output_style_manager.deploy_output_style(output_style_content)
|
|
82
82
|
|
|
83
83
|
if deployed:
|
|
84
|
-
self.logger.info("✅ Output style deployed to Claude
|
|
84
|
+
self.logger.info("✅ Output style deployed to Claude Code >= 1.0.83")
|
|
85
85
|
else:
|
|
86
86
|
self.logger.info("📝 Output style will be injected into instructions for older Claude versions")
|
|
87
87
|
|
|
@@ -97,11 +97,11 @@ class FrameworkLoader:
|
|
|
97
97
|
# Claude version detection
|
|
98
98
|
claude_version = self.output_style_manager.claude_version
|
|
99
99
|
if claude_version:
|
|
100
|
-
self.logger.info(f"Claude
|
|
100
|
+
self.logger.info(f"Claude Code version detected: {claude_version}")
|
|
101
101
|
|
|
102
102
|
# Check if version supports output styles
|
|
103
103
|
if self.output_style_manager.supports_output_styles():
|
|
104
|
-
self.logger.info("✅ Claude
|
|
104
|
+
self.logger.info("✅ Claude Code supports output styles (>= 1.0.83)")
|
|
105
105
|
|
|
106
106
|
# Check deployment status
|
|
107
107
|
output_style_path = self.output_style_manager.output_style_path
|
|
@@ -111,10 +111,10 @@ class FrameworkLoader:
|
|
|
111
111
|
self.logger.info(f"📝 Output style will be created at: {output_style_path}")
|
|
112
112
|
|
|
113
113
|
else:
|
|
114
|
-
self.logger.info(f"⚠️ Claude
|
|
114
|
+
self.logger.info(f"⚠️ Claude Code {claude_version} does not support output styles (< 1.0.83)")
|
|
115
115
|
self.logger.info("📝 Output style content will be injected into framework instructions")
|
|
116
116
|
else:
|
|
117
|
-
self.logger.info("⚠️ Claude
|
|
117
|
+
self.logger.info("⚠️ Claude Code not detected or version unknown")
|
|
118
118
|
self.logger.info("📝 Output style content will be injected into framework instructions as fallback")
|
|
119
119
|
|
|
120
120
|
def _detect_framework_path(self) -> Optional[Path]:
|
|
@@ -311,17 +311,41 @@ class FrameworkLoader:
|
|
|
311
311
|
|
|
312
312
|
def _load_instructions_file(self, content: Dict[str, Any]) -> None:
|
|
313
313
|
"""
|
|
314
|
-
Load INSTRUCTIONS.md
|
|
314
|
+
Load custom INSTRUCTIONS.md from .claude-mpm directories.
|
|
315
315
|
|
|
316
|
-
|
|
316
|
+
Precedence (highest to lowest):
|
|
317
|
+
1. Project-specific: ./.claude-mpm/INSTRUCTIONS.md
|
|
318
|
+
2. User-specific: ~/.claude-mpm/INSTRUCTIONS.md
|
|
319
|
+
|
|
320
|
+
NOTE: We do NOT load CLAUDE.md files since Claude Code already picks them up automatically.
|
|
317
321
|
This prevents duplication of instructions.
|
|
318
322
|
|
|
319
323
|
Args:
|
|
320
324
|
content: Dictionary to update with loaded instructions
|
|
321
325
|
"""
|
|
322
|
-
#
|
|
323
|
-
|
|
324
|
-
|
|
326
|
+
# Check for project-specific INSTRUCTIONS.md first
|
|
327
|
+
project_instructions_path = Path.cwd() / ".claude-mpm" / "INSTRUCTIONS.md"
|
|
328
|
+
if project_instructions_path.exists():
|
|
329
|
+
loaded_content = self._try_load_file(
|
|
330
|
+
project_instructions_path, "project-specific INSTRUCTIONS.md"
|
|
331
|
+
)
|
|
332
|
+
if loaded_content:
|
|
333
|
+
content["custom_instructions"] = loaded_content
|
|
334
|
+
content["custom_instructions_level"] = "project"
|
|
335
|
+
self.logger.info("Using project-specific PM instructions from .claude-mpm/INSTRUCTIONS.md")
|
|
336
|
+
return
|
|
337
|
+
|
|
338
|
+
# Check for user-specific INSTRUCTIONS.md
|
|
339
|
+
user_instructions_path = Path.home() / ".claude-mpm" / "INSTRUCTIONS.md"
|
|
340
|
+
if user_instructions_path.exists():
|
|
341
|
+
loaded_content = self._try_load_file(
|
|
342
|
+
user_instructions_path, "user-specific INSTRUCTIONS.md"
|
|
343
|
+
)
|
|
344
|
+
if loaded_content:
|
|
345
|
+
content["custom_instructions"] = loaded_content
|
|
346
|
+
content["custom_instructions_level"] = "user"
|
|
347
|
+
self.logger.info("Using user-specific PM instructions from ~/.claude-mpm/INSTRUCTIONS.md")
|
|
348
|
+
return
|
|
325
349
|
|
|
326
350
|
def _load_workflow_instructions(self, content: Dict[str, Any]) -> None:
|
|
327
351
|
"""
|
|
@@ -1101,6 +1125,16 @@ class FrameworkLoader:
|
|
|
1101
1125
|
|
|
1102
1126
|
# Note: We don't add working directory CLAUDE.md here since Claude Code
|
|
1103
1127
|
# already picks it up automatically. This prevents duplication.
|
|
1128
|
+
|
|
1129
|
+
# Add custom INSTRUCTIONS.md if present (overrides or extends framework instructions)
|
|
1130
|
+
if self.framework_content.get("custom_instructions"):
|
|
1131
|
+
level = self.framework_content.get("custom_instructions_level", "unknown")
|
|
1132
|
+
instructions += f"\n\n## Custom PM Instructions ({level} level)\n\n"
|
|
1133
|
+
instructions += "**The following custom instructions override or extend the framework defaults:**\n\n"
|
|
1134
|
+
instructions += self._strip_metadata_comments(
|
|
1135
|
+
self.framework_content["custom_instructions"]
|
|
1136
|
+
)
|
|
1137
|
+
instructions += "\n"
|
|
1104
1138
|
|
|
1105
1139
|
# Add WORKFLOW.md after instructions
|
|
1106
1140
|
if self.framework_content.get("workflow_instructions"):
|
|
@@ -206,7 +206,7 @@ class InteractiveSession:
|
|
|
206
206
|
return self._attempt_fallback_launch(environment)
|
|
207
207
|
|
|
208
208
|
def process_interactive_command(self, prompt: str) -> Optional[bool]:
|
|
209
|
-
"""Process special interactive commands like /agents.
|
|
209
|
+
"""Process special interactive commands like /agents and /mpm-doctor.
|
|
210
210
|
|
|
211
211
|
Args:
|
|
212
212
|
prompt: User input command
|
|
@@ -214,9 +214,19 @@ class InteractiveSession:
|
|
|
214
214
|
Returns:
|
|
215
215
|
Optional[bool]: True if handled, False if error, None if not a special command
|
|
216
216
|
"""
|
|
217
|
+
# Parse command and arguments
|
|
218
|
+
parts = prompt.strip().split()
|
|
219
|
+
if not parts:
|
|
220
|
+
return None
|
|
221
|
+
|
|
222
|
+
command = parts[0]
|
|
223
|
+
args = parts[1:]
|
|
224
|
+
|
|
217
225
|
# Check for special commands
|
|
218
|
-
if
|
|
226
|
+
if command == "/agents":
|
|
219
227
|
return self._show_available_agents()
|
|
228
|
+
elif command == "/mpm-doctor":
|
|
229
|
+
return self._run_doctor_diagnostics(args)
|
|
220
230
|
|
|
221
231
|
# Not a special command
|
|
222
232
|
return None
|
|
@@ -310,7 +320,13 @@ class InteractiveSession:
|
|
|
310
320
|
print(f"\033[32m│\033[0m {output_style_info:<49}\033[32m│\033[0m")
|
|
311
321
|
print("\033[32m│ │\033[0m")
|
|
312
322
|
print(
|
|
313
|
-
"\033[32m│\033[0m
|
|
323
|
+
"\033[32m│\033[0m Commands: \033[32m│\033[0m"
|
|
324
|
+
)
|
|
325
|
+
print(
|
|
326
|
+
"\033[32m│\033[0m /agents - Show available agents \033[32m│\033[0m"
|
|
327
|
+
)
|
|
328
|
+
print(
|
|
329
|
+
"\033[32m│\033[0m /mpm-doctor - Run diagnostic checks \033[32m│\033[0m"
|
|
314
330
|
)
|
|
315
331
|
print("\033[32m╰───────────────────────────────────────────────────╯\033[0m")
|
|
316
332
|
print("") # Add blank line after box
|
|
@@ -552,3 +568,66 @@ class InteractiveSession:
|
|
|
552
568
|
except Exception as e:
|
|
553
569
|
print(f"Error getting agent versions: {e}")
|
|
554
570
|
return False
|
|
571
|
+
|
|
572
|
+
def _run_doctor_diagnostics(self, args: list) -> bool:
|
|
573
|
+
"""Run doctor diagnostics from interactive mode.
|
|
574
|
+
|
|
575
|
+
Args:
|
|
576
|
+
args: Command arguments (e.g., ['--verbose'])
|
|
577
|
+
|
|
578
|
+
Returns:
|
|
579
|
+
bool: True if successful, False otherwise
|
|
580
|
+
"""
|
|
581
|
+
try:
|
|
582
|
+
from claude_mpm.services.diagnostics import DiagnosticRunner, DoctorReporter
|
|
583
|
+
|
|
584
|
+
# Parse arguments
|
|
585
|
+
verbose = "--verbose" in args or "-v" in args
|
|
586
|
+
no_color = "--no-color" in args
|
|
587
|
+
|
|
588
|
+
# Print header
|
|
589
|
+
print("\n" + "="*60)
|
|
590
|
+
print("Claude MPM Doctor Report")
|
|
591
|
+
print("="*60)
|
|
592
|
+
|
|
593
|
+
# Create diagnostic runner
|
|
594
|
+
runner = DiagnosticRunner(verbose=verbose)
|
|
595
|
+
|
|
596
|
+
# Run diagnostics
|
|
597
|
+
try:
|
|
598
|
+
summary = runner.run_diagnostics()
|
|
599
|
+
except KeyboardInterrupt:
|
|
600
|
+
print("\nDiagnostics interrupted by user")
|
|
601
|
+
return False
|
|
602
|
+
except Exception as e:
|
|
603
|
+
print(f"\n❌ Diagnostic failed: {str(e)}")
|
|
604
|
+
if verbose:
|
|
605
|
+
import traceback
|
|
606
|
+
traceback.print_exc()
|
|
607
|
+
return False
|
|
608
|
+
|
|
609
|
+
# Create reporter
|
|
610
|
+
reporter = DoctorReporter(
|
|
611
|
+
use_color=not no_color,
|
|
612
|
+
verbose=verbose
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
# Display results in terminal format
|
|
616
|
+
reporter.report(summary, format="terminal")
|
|
617
|
+
|
|
618
|
+
# Return based on status
|
|
619
|
+
if summary.error_count > 0:
|
|
620
|
+
return False
|
|
621
|
+
|
|
622
|
+
return True
|
|
623
|
+
|
|
624
|
+
except ImportError as e:
|
|
625
|
+
print(f"Error: Diagnostics module not available: {e}")
|
|
626
|
+
print("Please ensure claude-mpm is properly installed")
|
|
627
|
+
return False
|
|
628
|
+
except Exception as e:
|
|
629
|
+
print(f"Error running diagnostics: {e}")
|
|
630
|
+
if "--verbose" in args or "-v" in args:
|
|
631
|
+
import traceback
|
|
632
|
+
traceback.print_exc()
|
|
633
|
+
return False
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
This module handles:
|
|
4
4
|
1. Claude version detection
|
|
5
5
|
2. Output style extraction from framework instructions
|
|
6
|
-
3. One-time deployment to Claude
|
|
6
|
+
3. One-time deployment to Claude Code >= 1.0.83 at startup
|
|
7
7
|
4. Fallback injection for older versions
|
|
8
8
|
|
|
9
9
|
The output style is set once at startup and not monitored or enforced after that.
|
|
@@ -41,7 +41,7 @@ class OutputStyleManager:
|
|
|
41
41
|
|
|
42
42
|
def _detect_claude_version(self) -> Optional[str]:
|
|
43
43
|
"""
|
|
44
|
-
Detect Claude
|
|
44
|
+
Detect Claude Code version by running 'claude --version'.
|
|
45
45
|
|
|
46
46
|
Returns:
|
|
47
47
|
Version string (e.g., "1.0.82") or None if Claude not found
|
|
@@ -73,7 +73,7 @@ class OutputStyleManager:
|
|
|
73
73
|
return None
|
|
74
74
|
|
|
75
75
|
except FileNotFoundError:
|
|
76
|
-
self.logger.info("Claude
|
|
76
|
+
self.logger.info("Claude Code not found in PATH")
|
|
77
77
|
return None
|
|
78
78
|
except subprocess.TimeoutExpired:
|
|
79
79
|
self.logger.warning("Claude version check timed out")
|
|
@@ -116,7 +116,7 @@ class OutputStyleManager:
|
|
|
116
116
|
|
|
117
117
|
def supports_output_styles(self) -> bool:
|
|
118
118
|
"""
|
|
119
|
-
Check if Claude
|
|
119
|
+
Check if Claude Code supports output styles (>= 1.0.83).
|
|
120
120
|
|
|
121
121
|
Returns:
|
|
122
122
|
True if Claude version >= 1.0.83, False otherwise
|
|
@@ -324,7 +324,7 @@ class OutputStyleManager:
|
|
|
324
324
|
|
|
325
325
|
def deploy_output_style(self, content: str) -> bool:
|
|
326
326
|
"""
|
|
327
|
-
Deploy output style to Claude
|
|
327
|
+
Deploy output style to Claude Code if version >= 1.0.83.
|
|
328
328
|
Deploys the style file and activates it once.
|
|
329
329
|
|
|
330
330
|
Args:
|
|
@@ -356,7 +356,7 @@ class OutputStyleManager:
|
|
|
356
356
|
|
|
357
357
|
def _activate_output_style(self) -> bool:
|
|
358
358
|
"""
|
|
359
|
-
Update Claude
|
|
359
|
+
Update Claude Code settings to activate the claude-mpm output style.
|
|
360
360
|
Sets activeOutputStyle to "claude-mpm" once at startup.
|
|
361
361
|
|
|
362
362
|
Returns:
|
claude_mpm/core/socketio_pool.py
CHANGED
|
@@ -35,8 +35,8 @@ except ImportError:
|
|
|
35
35
|
# Fallback if constants module not available
|
|
36
36
|
class NetworkConfig:
|
|
37
37
|
DEFAULT_DASHBOARD_PORT = 8765
|
|
38
|
-
SOCKETIO_PORT_RANGE = (
|
|
39
|
-
DEFAULT_SOCKETIO_PORT =
|
|
38
|
+
SOCKETIO_PORT_RANGE = (8765, 8785)
|
|
39
|
+
DEFAULT_SOCKETIO_PORT = 8765
|
|
40
40
|
|
|
41
41
|
socketio = None
|
|
42
42
|
|
claude_mpm/core/unified_paths.py
CHANGED
|
@@ -641,6 +641,128 @@ class UnifiedPathManager:
|
|
|
641
641
|
if src_dir.exists() and str(src_dir) not in sys.path:
|
|
642
642
|
sys.path.insert(0, str(src_dir))
|
|
643
643
|
|
|
644
|
+
def get_executable_path(self) -> Optional[Path]:
|
|
645
|
+
"""Get the path to the claude-mpm executable for the current deployment context.
|
|
646
|
+
|
|
647
|
+
This method provides deployment-context-aware executable path detection,
|
|
648
|
+
particularly useful for MCP server configuration.
|
|
649
|
+
|
|
650
|
+
Returns:
|
|
651
|
+
Path to executable or None if not found
|
|
652
|
+
"""
|
|
653
|
+
import shutil
|
|
654
|
+
|
|
655
|
+
# Try standard which first
|
|
656
|
+
which_result = shutil.which("claude-mpm")
|
|
657
|
+
if which_result:
|
|
658
|
+
return Path(which_result)
|
|
659
|
+
|
|
660
|
+
# Enhanced detection based on deployment context
|
|
661
|
+
if self._deployment_context == DeploymentContext.PIPX_INSTALL:
|
|
662
|
+
return self._find_pipx_executable()
|
|
663
|
+
elif self._deployment_context in (DeploymentContext.DEVELOPMENT, DeploymentContext.EDITABLE_INSTALL):
|
|
664
|
+
return self._find_development_executable()
|
|
665
|
+
elif self._deployment_context == DeploymentContext.PIP_INSTALL:
|
|
666
|
+
return self._find_pip_executable()
|
|
667
|
+
|
|
668
|
+
return None
|
|
669
|
+
|
|
670
|
+
def _find_pipx_executable(self) -> Optional[Path]:
|
|
671
|
+
"""Find claude-mpm executable in pipx installation."""
|
|
672
|
+
try:
|
|
673
|
+
import claude_mpm
|
|
674
|
+
module_path = Path(claude_mpm.__file__).parent
|
|
675
|
+
|
|
676
|
+
if "pipx" not in str(module_path):
|
|
677
|
+
return None
|
|
678
|
+
|
|
679
|
+
# Common pipx executable locations
|
|
680
|
+
home = Path.home()
|
|
681
|
+
pipx_paths = [
|
|
682
|
+
home / ".local" / "bin" / "claude-mpm",
|
|
683
|
+
home / ".local" / "share" / "pipx" / "venvs" / "claude-mpm" / "bin" / "claude-mpm",
|
|
684
|
+
]
|
|
685
|
+
|
|
686
|
+
# Windows paths
|
|
687
|
+
if sys.platform == "win32":
|
|
688
|
+
pipx_paths.extend([
|
|
689
|
+
home / "AppData" / "Local" / "pipx" / "bin" / "claude-mpm.exe",
|
|
690
|
+
home / ".local" / "bin" / "claude-mpm.exe",
|
|
691
|
+
])
|
|
692
|
+
|
|
693
|
+
for path in pipx_paths:
|
|
694
|
+
if path.exists():
|
|
695
|
+
logger.debug(f"Found pipx executable: {path}")
|
|
696
|
+
return path
|
|
697
|
+
|
|
698
|
+
# Try to derive from module path
|
|
699
|
+
# Navigate up from module to find venv, then to bin
|
|
700
|
+
venv_path = module_path
|
|
701
|
+
for _ in range(5): # Prevent infinite loops
|
|
702
|
+
if venv_path.name == "claude-mpm" and (venv_path / "pyvenv.cfg").exists():
|
|
703
|
+
# Found the venv directory
|
|
704
|
+
bin_dir = venv_path / ("Scripts" if sys.platform == "win32" else "bin")
|
|
705
|
+
exe_name = "claude-mpm.exe" if sys.platform == "win32" else "claude-mpm"
|
|
706
|
+
exe_path = bin_dir / exe_name
|
|
707
|
+
|
|
708
|
+
if exe_path.exists():
|
|
709
|
+
logger.debug(f"Found pipx executable via module path: {exe_path}")
|
|
710
|
+
return exe_path
|
|
711
|
+
break
|
|
712
|
+
|
|
713
|
+
if venv_path == venv_path.parent:
|
|
714
|
+
break
|
|
715
|
+
venv_path = venv_path.parent
|
|
716
|
+
|
|
717
|
+
except Exception as e:
|
|
718
|
+
logger.debug(f"Error finding pipx executable: {e}")
|
|
719
|
+
|
|
720
|
+
return None
|
|
721
|
+
|
|
722
|
+
def _find_development_executable(self) -> Optional[Path]:
|
|
723
|
+
"""Find claude-mpm executable in development installation."""
|
|
724
|
+
# For development, prefer the script in the project
|
|
725
|
+
scripts_dir = self.get_scripts_dir()
|
|
726
|
+
dev_executable = scripts_dir / "claude-mpm"
|
|
727
|
+
|
|
728
|
+
if dev_executable.exists():
|
|
729
|
+
return dev_executable
|
|
730
|
+
|
|
731
|
+
# Check if we're in a development venv
|
|
732
|
+
if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
|
|
733
|
+
venv_bin = Path(sys.prefix) / ("Scripts" if sys.platform == "win32" else "bin")
|
|
734
|
+
venv_executable = venv_bin / "claude-mpm"
|
|
735
|
+
if venv_executable.exists():
|
|
736
|
+
return venv_executable
|
|
737
|
+
|
|
738
|
+
return None
|
|
739
|
+
|
|
740
|
+
def _find_pip_executable(self) -> Optional[Path]:
|
|
741
|
+
"""Find claude-mpm executable in pip installation."""
|
|
742
|
+
# For pip installs, check the current Python environment
|
|
743
|
+
if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix):
|
|
744
|
+
# In a virtual environment
|
|
745
|
+
venv_bin = Path(sys.prefix) / ("Scripts" if sys.platform == "win32" else "bin")
|
|
746
|
+
venv_executable = venv_bin / "claude-mpm"
|
|
747
|
+
if venv_executable.exists():
|
|
748
|
+
return venv_executable
|
|
749
|
+
|
|
750
|
+
# Check system-wide installation
|
|
751
|
+
try:
|
|
752
|
+
import site
|
|
753
|
+
for site_dir in site.getsitepackages():
|
|
754
|
+
# Look for installed scripts
|
|
755
|
+
site_path = Path(site_dir)
|
|
756
|
+
scripts_dir = site_path.parent / ("Scripts" if sys.platform == "win32" else "bin")
|
|
757
|
+
if scripts_dir.exists():
|
|
758
|
+
exe_path = scripts_dir / "claude-mpm"
|
|
759
|
+
if exe_path.exists():
|
|
760
|
+
return exe_path
|
|
761
|
+
except Exception as e:
|
|
762
|
+
logger.debug(f"Error finding pip executable: {e}")
|
|
763
|
+
|
|
764
|
+
return None
|
|
765
|
+
|
|
644
766
|
|
|
645
767
|
# ============================================================================
|
|
646
768
|
# Singleton Instance and Convenience Functions
|
|
@@ -701,6 +823,11 @@ def get_package_resource_path(resource_path: str) -> Path:
|
|
|
701
823
|
return get_path_manager().get_package_resource_path(resource_path)
|
|
702
824
|
|
|
703
825
|
|
|
826
|
+
def get_executable_path() -> Optional[Path]:
|
|
827
|
+
"""Get the claude-mpm executable path for the current deployment context."""
|
|
828
|
+
return get_path_manager().get_executable_path()
|
|
829
|
+
|
|
830
|
+
|
|
704
831
|
# ============================================================================
|
|
705
832
|
# Export All Public Symbols
|
|
706
833
|
# ============================================================================
|
|
@@ -719,4 +846,5 @@ __all__ = [
|
|
|
719
846
|
"get_config_dir",
|
|
720
847
|
"find_file_upwards",
|
|
721
848
|
"get_package_resource_path",
|
|
849
|
+
"get_executable_path",
|
|
722
850
|
]
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
class e{constructor(e,t){this.container=document.getElementById(e),this.socketClient=t,this.events=[],this.filteredEvents=[],this.selectedEventIndex=-1,this.filteredEventElements=[],this.autoScroll=!0,this.searchFilter="",this.typeFilter="",this.sessionFilter="",this.eventTypeCount={},this.availableEventTypes=new Set,this.errorCount=0,this.eventsThisMinute=0,this.lastMinute=(new Date).getMinutes(),this.init()}init(){this.setupEventHandlers(),this.setupKeyboardNavigation(),this.socketClient.onEventUpdate((e,t)=>{this.events=Array.isArray(e)?e:[],this.updateDisplay()})}setupEventHandlers(){const e=document.getElementById("events-search-input");e&&e.addEventListener("input",e=>{this.searchFilter=e.target.value.toLowerCase(),this.applyFilters()});const t=document.getElementById("events-type-filter");t&&t.addEventListener("change",e=>{this.typeFilter=e.target.value,this.applyFilters()})}setupKeyboardNavigation(){console.log("EventViewer: Keyboard navigation handled by unified Dashboard system")}handleArrowNavigation(e){if(0===this.filteredEventElements.length)return;let t=this.selectedEventIndex+e;t>=this.filteredEventElements.length?t=0:t<0&&(t=this.filteredEventElements.length-1),this.showEventDetails(t)}applyFilters(){this.events&&Array.isArray(this.events)||(console.warn("EventViewer: events array is not initialized, using empty array"),this.events=[]),this.filteredEvents=this.events.filter(e=>{if(this.searchFilter){if(![e.type||"",e.subtype||"",JSON.stringify(e.data||{})].join(" ").toLowerCase().includes(this.searchFilter))return!1}if(this.typeFilter){const t=e.type&&""!==e.type.trim()?e.type:"";if((e.subtype&&t?`${t}.${e.subtype}`:t)!==this.typeFilter)return!1}return!(this.sessionFilter&&""!==this.sessionFilter&&(!e.data||e.data.session_id!==this.sessionFilter))}),this.renderEvents(),this.updateMetrics()}updateEventTypeDropdown(){const e=document.getElementById("events-type-filter");if(!e)return;const t=new Set;this.events&&Array.isArray(this.events)||(console.warn("EventViewer: events array is not initialized in updateEventTypeDropdown"),this.events=[]),this.events.forEach(e=>{if(e.type&&""!==e.type.trim()){const n=e.subtype?`${e.type}.${e.subtype}`:e.type;t.add(n)}});const n=Array.from(t).sort(),s=Array.from(this.availableEventTypes).sort();if(JSON.stringify(n)===JSON.stringify(s))return;this.availableEventTypes=t;const i=e.value;e.innerHTML='<option value="">All Events</option>';Array.from(t).sort().forEach(t=>{const n=document.createElement("option");n.value=t,n.textContent=t,e.appendChild(n)}),i&&t.has(i)?e.value=i:i&&!t.has(i)&&(e.value="",this.typeFilter="")}updateDisplay(){this.updateEventTypeDropdown(),this.applyFilters()}renderEvents(){const e=document.getElementById("events-list");if(!e)return;if(0===this.filteredEvents.length)return e.innerHTML=`\n <div class="no-events">\n ${0===this.events.length?"Connect to Socket.IO server to see events...":"No events match current filters..."}\n </div>\n `,void(this.filteredEventElements=[]);const t=this.filteredEvents.map((e,t)=>{const n=new Date(e.timestamp).toLocaleTimeString();return`\n <div class="event-item single-row ${e.type?`event-${e.type}`:"event-default"} ${t===this.selectedEventIndex?"selected":""}"\n onclick="eventViewer.showEventDetails(${t})"\n data-index="${t}">\n <span class="event-single-row-content">\n <span class="event-content-main">${this.formatSingleRowEventContent(e)}</span>\n <span class="event-timestamp">${n}</span>\n </span>\n ${this.createInlineEditDiffViewer(e,t)}\n </div>\n `}).join("");e.innerHTML=t,this.filteredEventElements=Array.from(e.querySelectorAll(".event-item")),window.dashboard&&"events"===window.dashboard.currentTab&&window.dashboard.tabNavigation&&window.dashboard.tabNavigation.events&&(window.dashboard.tabNavigation.events.items=this.filteredEventElements),this.autoScroll&&this.filteredEvents.length>0&&(e.scrollTop=e.scrollHeight)}formatEventType(e){return e.type&&e.subtype?`${e.type}.${e.subtype}`:e.type?e.type:e.originalEventName?e.originalEventName:"unknown"}formatEventData(e){if(!e.data)return"No data";switch(e.type){case"session":return this.formatSessionEvent(e);case"claude":return this.formatClaudeEvent(e);case"agent":return this.formatAgentEvent(e);case"hook":return this.formatHookEvent(e);case"todo":return this.formatTodoEvent(e);case"memory":return this.formatMemoryEvent(e);case"log":return this.formatLogEvent(e);default:return this.formatGenericEvent(e)}}formatSessionEvent(e){const t=e.data;return"started"===e.subtype?`<strong>Session started:</strong> ${t.session_id||"Unknown"}`:"ended"===e.subtype?`<strong>Session ended:</strong> ${t.session_id||"Unknown"}`:`<strong>Session:</strong> ${JSON.stringify(t)}`}formatClaudeEvent(e){const t=e.data;if("request"===e.subtype){const e=t.prompt||t.message||"";return`<strong>Request:</strong> ${e.length>100?e.substring(0,100)+"...":e}`}if("response"===e.subtype){const e=t.response||t.content||"";return`<strong>Response:</strong> ${e.length>100?e.substring(0,100)+"...":e}`}return`<strong>Claude:</strong> ${JSON.stringify(t)}`}formatAgentEvent(e){const t=e.data;return"loaded"===e.subtype?`<strong>Agent loaded:</strong> ${t.agent_type||t.name||"Unknown"}`:"executed"===e.subtype?`<strong>Agent executed:</strong> ${t.agent_type||t.name||"Unknown"}`:`<strong>Agent:</strong> ${JSON.stringify(t)}`}formatHookEvent(e){const t=e.data,n=t.event_type||e.subtype||"unknown";switch(n){case"user_prompt":const s=t.prompt_text||t.prompt_preview||"";return`<strong>User Prompt:</strong> ${(s.length>80?s.substring(0,80)+"...":s)||"No prompt text"}`;case"pre_tool":const i=t.tool_name||"Unknown tool";return`<strong>Pre-Tool (${t.operation_type||"operation"}):</strong> ${i}`;case"post_tool":const o=t.tool_name||"Unknown tool";return`<strong>Post-Tool (${t.success?"success":t.status||"failed"}):</strong> ${o}${t.duration_ms?` (${t.duration_ms}ms)`:""}`;case"notification":return`<strong>Notification (${t.notification_type||"notification"}):</strong> ${t.message_preview||t.message||"No message"}`;case"stop":const r=t.reason||"unknown";return`<strong>Stop (${t.stop_type||"normal"}):</strong> ${r}`;case"subagent_stop":return`<strong>Subagent Stop (${t.agent_type||"unknown agent"}):</strong> ${t.reason||"unknown"}`;default:const a=t.hook_name||t.name||t.event_type||"Unknown";return`<strong>Hook ${e.subtype||n}:</strong> ${a}`}}formatTodoEvent(e){const t=e.data;if(t.todos&&Array.isArray(t.todos)){const e=t.todos.length;return`<strong>Todo updated:</strong> ${e} item${1!==e?"s":""}`}return`<strong>Todo:</strong> ${JSON.stringify(t)}`}formatMemoryEvent(e){const t=e.data;return`<strong>Memory ${t.operation||"unknown"}:</strong> ${t.key||"Unknown key"}`}formatLogEvent(e){const t=e.data,n=t.level||"info",s=t.message||"",i=s.length>80?s.substring(0,80)+"...":s;return`<strong>[${n.toUpperCase()}]</strong> ${i}`}formatGenericEvent(e){const t=e.data;return"string"==typeof t?t.length>100?t.substring(0,100)+"...":t:JSON.stringify(t)}formatSingleRowEventContent(e){const t=this.formatEventType(e),n=e.data||{};let s="",i="";switch(e.type){case"hook":const t=e.tool_name||n.tool_name||"Unknown",o=e.subtype||"Unknown",r=this.getHookDisplayName(o,n);i=this.getEventCategory(e),s=`${r} (${i}): ${t}`;break;case"agent":i="agent_operations",s=`${e.subagent_type||n.subagent_type||"PM"} ${e.subtype||"action"}`;break;case"todo":i="task_management",s=`TodoWrite (${n.todos?n.todos.length:0} items)`;break;case"memory":i="memory_operations",s=`${n.operation||"unknown"} ${n.key||"unknown"}`;break;case"session":i="session_management",s=`Session ${e.subtype||"unknown"}`;break;case"claude":i="claude_interactions",s=`Claude ${e.subtype||"interaction"}`;break;default:i="general",s=e.type||"Unknown Event"}return`${t} ${s}`}getHookDisplayName(e,t){return{pre_tool:"Pre-Tool",post_tool:"Post-Tool",user_prompt:"User-Prompt",stop:"Stop",subagent_stop:"Subagent-Stop",notification:"Notification"}[e]||e.replace("_","-")}getEventCategory(e){const t=e.data||{},n=e.tool_name||t.tool_name||"";return["Read","Write","Edit","MultiEdit"].includes(n)?"file_operations":["Bash","grep","Glob"].includes(n)?"system_operations":"TodoWrite"===n?"task_management":"Task"===n?"agent_delegation":"stop"===e.subtype||"subagent_stop"===e.subtype?"session_control":"general"}showEventDetails(e){if(!this.filteredEvents||!Array.isArray(this.filteredEvents))return void console.warn("EventViewer: filteredEvents array is not initialized");if(e<0||e>=this.filteredEvents.length)return;this.selectedEventIndex=e;const t=this.filteredEvents[e];window.dashboard&&(window.dashboard.tabNavigation&&window.dashboard.tabNavigation.events&&(window.dashboard.tabNavigation.events.selectedIndex=e),window.dashboard.selectCard&&window.dashboard.selectCard("events",e,"event",t)),this.filteredEventElements.forEach((t,n)=>{t.classList.toggle("selected",n===e)}),document.dispatchEvent(new CustomEvent("eventSelected",{detail:{event:t,index:e}}));const n=this.filteredEventElements[e];n&&n.scrollIntoView({behavior:"smooth",block:"nearest"})}clearSelection(){this.selectedEventIndex=-1,this.filteredEventElements.forEach(e=>{e.classList.remove("selected")}),window.dashboard&&(window.dashboard.tabNavigation&&window.dashboard.tabNavigation.events&&(window.dashboard.tabNavigation.events.selectedIndex=-1),window.dashboard.clearCardSelection&&window.dashboard.clearCardSelection()),document.dispatchEvent(new CustomEvent("eventSelectionCleared"))}updateMetrics(){this.eventTypeCount={},this.errorCount=0,this.events&&Array.isArray(this.events)||(console.warn("EventViewer: events array is not initialized in updateMetrics"),this.events=[]),this.events.forEach(e=>{const t=e.type||"unknown";this.eventTypeCount[t]=(this.eventTypeCount[t]||0)+1,"log"===e.type&&e.data&&["error","critical"].includes(e.data.level)&&this.errorCount++});const e=(new Date).getMinutes();e!==this.lastMinute&&(this.lastMinute=e,this.eventsThisMinute=0);const t=new Date(Date.now()-6e4);this.eventsThisMinute=this.events.filter(e=>new Date(e.timestamp)>t).length,this.updateMetricsUI()}updateMetricsUI(){const e=document.getElementById("total-events"),t=document.getElementById("events-per-minute"),n=document.getElementById("unique-types"),s=document.getElementById("error-count");e&&(e.textContent=this.events.length),t&&(t.textContent=this.eventsThisMinute),n&&(n.textContent=Object.keys(this.eventTypeCount).length),s&&(s.textContent=this.errorCount)}exportEvents(){const e=JSON.stringify(this.filteredEvents,null,2),t=new Blob([e],{type:"application/json"}),n=URL.createObjectURL(t),s=document.createElement("a");s.href=n,s.download=`claude-mpm-events-${(new Date).toISOString().split("T")[0]}.json`,s.click(),URL.revokeObjectURL(n)}clearEvents(){this.socketClient.clearEvents(),this.selectedEventIndex=-1,this.updateDisplay()}setSessionFilter(e){this.sessionFilter=e,this.applyFilters()}getFilters(){return{search:this.searchFilter,type:this.typeFilter,session:this.sessionFilter}}getFilteredEvents(){return this.filteredEvents}getAllEvents(){return this.events}createInlineEditDiffViewer(e,t){const n=e.data||{},s=e.tool_name||n.tool_name||"";if(!["Edit","MultiEdit"].includes(s))return"";let i=[];if("Edit"===s){const t=e.tool_parameters||n.tool_parameters||{};t.old_string&&t.new_string&&i.push({old_string:t.old_string,new_string:t.new_string,file_path:t.file_path||"unknown"})}else if("MultiEdit"===s){const t=e.tool_parameters||n.tool_parameters||{};t.edits&&Array.isArray(t.edits)&&(i=t.edits.map(e=>({...e,file_path:t.file_path||"unknown"})))}if(0===i.length)return"";const o=`edit-diff-${t}`,r=i.length>1;let a="";return i.forEach((e,t)=>{const n=this.createDiffHtml(e.old_string,e.new_string);a+=`\n <div class="edit-diff-section">\n ${r?`<div class="edit-diff-header">Edit ${t+1}</div>`:""}\n <div class="diff-content">${n}</div>\n </div>\n `}),`\n <div class="inline-edit-diff-viewer">\n <div class="diff-toggle-header" onclick="eventViewer.toggleEditDiff('${o}', event)">\n <span class="diff-toggle-icon">📋</span>\n <span class="diff-toggle-text">Show ${r?i.length+" edits":"edit"}</span>\n <span class="diff-toggle-arrow">▼</span>\n </div>\n <div id="${o}" class="diff-content-container" style="display: none;">\n ${a}\n </div>\n </div>\n `}createDiffHtml(e,t){const n=e.split("\n"),s=t.split("\n");let i="",o=0,r=0;for(;o<n.length||r<s.length;){const e=o<n.length?n[o]:null,t=r<s.length?s[r]:null;null===e?(i+=`<div class="diff-line diff-added">+ ${this.escapeHtml(t)}</div>`,r++):null===t?(i+=`<div class="diff-line diff-removed">- ${this.escapeHtml(e)}</div>`,o++):e===t?(i+=`<div class="diff-line diff-unchanged"> ${this.escapeHtml(e)}</div>`,o++,r++):(i+=`<div class="diff-line diff-removed">- ${this.escapeHtml(e)}</div>`,i+=`<div class="diff-line diff-added">+ ${this.escapeHtml(t)}</div>`,o++,r++)}return`<div class="diff-container">${i}</div>`}toggleEditDiff(e,t){t.stopPropagation();const n=document.getElementById(e),s=t.currentTarget.querySelector(".diff-toggle-arrow");if(n){const e="none"!==n.style.display;n.style.display=e?"none":"block",s&&(s.textContent=e?"▼":"▲")}}escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}}window.EventViewer=e;class t{constructor(e,t){this.eventViewer=e,this.agentInference=t,this.agentEvents=[],this.filteredAgentEvents=[],this.filteredToolEvents=[],this.filteredFileEvents=[],this.selectedSessionId=null,this.fileTrackingCache=new Map,this.trackingCheckTimeout=3e4,console.log("Event processor initialized")}getFilteredEventsForTab(e){const t=this.eventViewer.events;console.log(`getFilteredEventsForTab(${e}) - using RAW events: ${t.length} total`);const n=window.sessionManager;if(n&&n.selectedSessionId){const e=n.getEventsForSession(n.selectedSessionId);return console.log(`Filtering by session ${n.selectedSessionId}: ${e.length} events`),e}return t}applyAgentsFilters(e){const t=document.getElementById("agents-search-input"),n=document.getElementById("agents-type-filter"),s=t?t.value.toLowerCase():"",i=n?n.value:"";return e.filter(e=>{if(s){if(![e.agentName||"",e.type||"",e.isImplied?"implied":"explicit"].join(" ").toLowerCase().includes(s))return!1}if(i){if(!(e.agentName||"unknown").toLowerCase().includes(i.toLowerCase()))return!1}return!0})}applyToolsFilters(e){const t=document.getElementById("tools-search-input"),n=document.getElementById("tools-type-filter"),s=t?t.value.toLowerCase():"",i=n?n.value:"";return e.filter(e=>{if(s){if(![e.tool_name||"",e.agent_type||"",e.type||"",e.subtype||""].join(" ").toLowerCase().includes(s))return!1}if(i){if((e.tool_name||"")!==i)return!1}return!0})}applyToolCallFilters(e){const t=document.getElementById("tools-search-input"),n=document.getElementById("tools-type-filter"),s=t?t.value.toLowerCase():"",i=n?n.value:"";return e.filter(([e,t])=>{if(s){if(![t.tool_name||"",t.agent_type||"","tool_call"].join(" ").toLowerCase().includes(s))return!1}if(i){if((t.tool_name||"")!==i)return!1}return!0})}applyFilesFilters(e){const t=document.getElementById("files-search-input"),n=document.getElementById("files-type-filter"),s=t?t.value.toLowerCase():"",i=n?n.value:"";return e.filter(([e,t])=>{if(this.selectedSessionId){const e=t.operations.filter(e=>e.sessionId===this.selectedSessionId);if(0===e.length)return!1;t={...t,operations:e,lastOperation:e[e.length-1]?.timestamp||t.lastOperation}}if(s){if(![e,...t.operations.map(e=>e.operation),...t.operations.map(e=>e.agent)].join(" ").toLowerCase().includes(s))return!1}if(i){if(!t.operations.map(e=>e.operation).includes(i))return!1}return!0})}extractOperation(e){if(!e)return"unknown";const t=e.toLowerCase();return t.includes("read")?"read":t.includes("write")?"write":t.includes("edit")?"edit":t.includes("create")?"create":t.includes("delete")?"delete":t.includes("move")||t.includes("rename")?"move":"other"}extractToolFromHook(e){if(!e)return"";const t=e.match(/^(?:Pre|Post)(.+)Use$/);return t?t[1]:""}extractToolFromSubtype(e){if(!e)return"";if(e.includes("_")){return e.split("_")[0]||""}return e}extractToolTarget(e,t,n){const s=t||n||{};switch(e?.toLowerCase()){case"read":case"write":case"edit":return s.file_path||s.path||"";case"bash":return s.command||"";case"grep":return s.pattern||"";case"task":return s.subagent_type||s.agent_type||"";default:const e=Object.keys(s),t=["path","file_path","command","pattern","query","target"];for(const n of t)if(s[n])return s[n];return e.length>0?`${e[0]}: ${s[e[0]]}`:""}}generateAgentHTML(e){const t=this.agentInference.getUniqueAgentInstances();return this.applyAgentsFilters(t).map((e,t)=>{const n=e.agentName,s=this.formatTimestamp(e.firstTimestamp||e.timestamp),i=e.isImplied?"implied":"explicit",o=e.totalEventCount||e.eventCount||0;return`\n <div class="event-item single-row event-agent" onclick="${`dashboard.selectCard('agents', ${t}, 'agent_instance', '${e.id}'); dashboard.showAgentInstanceDetails('${e.id}');`}">\n <span class="event-single-row-content">\n <span class="event-content-main">${`${n} (${i}, ${o} events)`}</span>\n <span class="event-timestamp">${s}</span>\n </span>\n </div>\n `}).join("")}generateToolHTML(e){return this.applyToolCallFilters(e).map(([e,t],n)=>{const s=t.tool_name||"Unknown",i=t.agent_type||"Unknown",o=this.formatTimestamp(t.timestamp);return`\n <div class="event-item single-row event-tool ${"completed"===(t.post_event?"completed":"pending")?"status-success":"status-pending"}" onclick="dashboard.selectCard('tools', ${n}, 'toolCall', '${e}'); dashboard.showToolCallDetails('${e}')">\n <span class="event-single-row-content">\n <span class="event-content-main">${`${s} (${"pm"===i.toLowerCase()?"pm":i})`}</span>\n <span class="event-timestamp">${o}</span>\n </span>\n </div>\n `}).join("")}generateFileHTML(e){return this.applyFilesFilters(e).map(([e,t],n)=>{const s=t.operations.map(e=>e.operation),i=this.formatTimestamp(t.lastOperation),o={};s.forEach(e=>{o[e]=(o[e]||0)+1});const r=Object.entries(o).map(([e,t])=>`${e}(${t})`).join(", "),a=[...new Set(t.operations.map(e=>e.agent))],l=a.length>1?`by ${a.length} agents`:`by ${a[0]||"unknown"}`;return`\n <div class="event-item single-row file-item" onclick="dashboard.selectCard('files', ${n}, 'file', '${e}'); dashboard.showFileDetails('${e}')">\n <span class="event-single-row-content">\n <span class="event-content-main">${`${this.getRelativeFilePath(e)} ${r} ${l}`}</span>\n <span class="event-timestamp">${i}</span>\n </span>\n </div>\n `}).join("")}getFileOperationIcon(e){return e.includes("write")||e.includes("create")?"📝":e.includes("edit")?"✏️":e.includes("read")?"👁️":e.includes("delete")?"🗑️":e.includes("move")?"📦":"📄"}getRelativeFilePath(e){if(!e)return"";const t=e.split("/");return t.length>3?".../"+t.slice(-2).join("/"):e}formatTimestamp(e){if(!e)return"";return new Date(e).toLocaleTimeString()}setSelectedSessionId(e){this.selectedSessionId=e}getSelectedSessionId(){return this.selectedSessionId}getUniqueToolInstances(e){return this.applyToolCallFilters(e)}getUniqueFileInstances(e){return this.applyFilesFilters(e)}async isFileTracked(e,t){const n=`${t}:${e}`,s=Date.now(),i=this.fileTrackingCache.get(n);if(i&&s-i.timestamp<this.trackingCheckTimeout)return i.is_tracked;try{const i=window.socket;return i?new Promise(o=>{const r=t=>{if(t.file_path===e){const e=t.success&&t.is_tracked;this.fileTrackingCache.set(n,{is_tracked:e,timestamp:s}),i.off("file_tracked_response",r),o(e)}};i.on("file_tracked_response",r),i.emit("check_file_tracked",{file_path:e,working_dir:t}),setTimeout(()=>{i.off("file_tracked_response",r),o(!1)},5e3)}):(console.warn("No socket connection available for git tracking check"),!1)}catch(o){return console.error("Error checking file tracking status:",o),!1}}generateGitDiffIcon(e,t,n){const s=`git-icon-${e.replace(/[^a-zA-Z0-9]/g,"-")}-${t}`,i=`\n <span id="${s}" class="git-diff-icon"\n onclick="event.stopPropagation(); showGitDiffModal('${e}', '${t}')"\n title="View git diff for this file operation"\n style="margin-left: 8px; cursor: pointer; font-size: 16px;">\n 📋\n </span>\n `;return this.isFileTracked(e,n).then(e=>{const t=document.getElementById(s);t&&(e?(t.innerHTML="📋",t.title="View git diff for this file operation",t.classList.add("tracked-file")):(t.innerHTML="📋❌",t.title="File not tracked by git - click to see details",t.classList.add("untracked-file")))}).catch(e=>{console.error("Error updating git diff icon:",e)}),i}showAgentInstanceDetails(e){const t=this.agentInference.getPMDelegations().get(e);if(!t)return void console.error("Agent instance not found:",e);console.log("Showing agent instance details for:",e,t);const n=`\n <div class="agent-instance-details">\n <h3>Agent Instance: ${t.agentName}</h3>\n <p><strong>Type:</strong> ${t.isImplied?"Implied PM Delegation":"Explicit PM Delegation"}</p>\n <p><strong>Start Time:</strong> ${this.formatTimestamp(t.timestamp)}</p>\n <p><strong>Event Count:</strong> ${t.agentEvents.length}</p>\n <p><strong>Session:</strong> ${t.sessionId}</p>\n ${t.pmCall?`<p><strong>PM Call:</strong> Task delegation to ${t.agentName}</p>`:"<p><strong>Note:</strong> Implied delegation (no explicit PM call found)</p>"}\n </div>\n `;console.log("Agent instance details HTML:",n)}}export{e as E,t as a};
|
|
1
|
+
class e{constructor(e,t){this.container=document.getElementById(e),this.socketClient=t,this.events=[],this.filteredEvents=[],this.selectedEventIndex=-1,this.filteredEventElements=[],this.autoScroll=!0,this.searchFilter="",this.typeFilter="",this.sessionFilter="",this.eventTypeCount={},this.availableEventTypes=new Set,this.errorCount=0,this.eventsThisMinute=0,this.lastMinute=(new Date).getMinutes(),this.init()}init(){this.setupEventHandlers(),this.setupKeyboardNavigation(),this.socketClient.onEventUpdate((e,t)=>{this.events=Array.isArray(e)?e:[],this.updateDisplay()})}setupEventHandlers(){const e=document.getElementById("events-search-input");e&&e.addEventListener("input",e=>{this.searchFilter=e.target.value.toLowerCase(),this.applyFilters()});const t=document.getElementById("events-type-filter");t&&t.addEventListener("change",e=>{this.typeFilter=e.target.value,this.applyFilters()})}setupKeyboardNavigation(){console.log("EventViewer: Keyboard navigation handled by unified Dashboard system")}handleArrowNavigation(e){if(0===this.filteredEventElements.length)return;let t=this.selectedEventIndex+e;t>=this.filteredEventElements.length?t=0:t<0&&(t=this.filteredEventElements.length-1),this.showEventDetails(t)}applyFilters(){this.events&&Array.isArray(this.events)||(console.warn("EventViewer: events array is not initialized, using empty array"),this.events=[]),this.filteredEvents=this.events.filter(e=>{if(this.searchFilter){if(![e.type||"",e.subtype||"",JSON.stringify(e.data||{})].join(" ").toLowerCase().includes(this.searchFilter))return!1}if(this.typeFilter){const t=e.type&&""!==e.type.trim()?e.type:"";if((e.subtype&&t?`${t}.${e.subtype}`:t)!==this.typeFilter)return!1}return!(this.sessionFilter&&""!==this.sessionFilter&&(!e.data||e.data.session_id!==this.sessionFilter))}),this.renderEvents(),this.updateMetrics()}updateEventTypeDropdown(){const e=document.getElementById("events-type-filter");if(!e)return;const t=new Set;this.events&&Array.isArray(this.events)||(console.warn("EventViewer: events array is not initialized in updateEventTypeDropdown"),this.events=[]),this.events.forEach(e=>{if(e.type&&""!==e.type.trim()){const n=e.subtype?`${e.type}.${e.subtype}`:e.type;t.add(n)}});const n=Array.from(t).sort(),s=Array.from(this.availableEventTypes).sort();if(JSON.stringify(n)===JSON.stringify(s))return;this.availableEventTypes=t;const i=e.value;e.innerHTML='<option value="">All Events</option>';Array.from(t).sort().forEach(t=>{const n=document.createElement("option");n.value=t,n.textContent=t,e.appendChild(n)}),i&&t.has(i)?e.value=i:i&&!t.has(i)&&(e.value="",this.typeFilter="")}updateDisplay(){this.updateEventTypeDropdown(),this.applyFilters()}renderEvents(){const e=document.getElementById("events-list");if(!e)return;if(0===this.filteredEvents.length)return e.innerHTML=`\n <div class="no-events">\n ${0===this.events.length?"Connect to Socket.IO server to see events...":"No events match current filters..."}\n </div>\n `,void(this.filteredEventElements=[]);const t=this.filteredEvents.map((e,t)=>{const n=new Date(e.timestamp).toLocaleTimeString();return`\n <div class="event-item single-row ${e.type?`event-${e.type}`:"event-default"} ${t===this.selectedEventIndex?"selected":""}"\n onclick="eventViewer.showEventDetails(${t})"\n data-index="${t}">\n <span class="event-single-row-content">\n <span class="event-content-main">${this.formatSingleRowEventContent(e)}</span>\n <span class="event-timestamp">${n}</span>\n </span>\n ${this.createInlineEditDiffViewer(e,t)}\n </div>\n `}).join("");e.innerHTML=t,this.filteredEventElements=Array.from(e.querySelectorAll(".event-item")),window.dashboard&&"events"===window.dashboard.currentTab&&window.dashboard.tabNavigation&&window.dashboard.tabNavigation.events&&(window.dashboard.tabNavigation.events.items=this.filteredEventElements),this.autoScroll&&this.filteredEvents.length>0&&(e.scrollTop=e.scrollHeight)}formatEventType(e){return e.type&&e.subtype?e.type===e.subtype?e.type:`${e.type}.${e.subtype}`:e.type?e.type:e.originalEventName?e.originalEventName:"unknown"}formatEventData(e){if(!e.data)return"No data";switch(e.type){case"session":return this.formatSessionEvent(e);case"claude":return this.formatClaudeEvent(e);case"agent":return this.formatAgentEvent(e);case"hook":return this.formatHookEvent(e);case"todo":return this.formatTodoEvent(e);case"memory":return this.formatMemoryEvent(e);case"log":return this.formatLogEvent(e);default:return this.formatGenericEvent(e)}}formatSessionEvent(e){const t=e.data;return"started"===e.subtype?`<strong>Session started:</strong> ${t.session_id||"Unknown"}`:"ended"===e.subtype?`<strong>Session ended:</strong> ${t.session_id||"Unknown"}`:`<strong>Session:</strong> ${JSON.stringify(t)}`}formatClaudeEvent(e){const t=e.data;if("request"===e.subtype){const e=t.prompt||t.message||"";return`<strong>Request:</strong> ${e.length>100?e.substring(0,100)+"...":e}`}if("response"===e.subtype){const e=t.response||t.content||"";return`<strong>Response:</strong> ${e.length>100?e.substring(0,100)+"...":e}`}return`<strong>Claude:</strong> ${JSON.stringify(t)}`}formatAgentEvent(e){const t=e.data;return"loaded"===e.subtype?`<strong>Agent loaded:</strong> ${t.agent_type||t.name||"Unknown"}`:"executed"===e.subtype?`<strong>Agent executed:</strong> ${t.agent_type||t.name||"Unknown"}`:`<strong>Agent:</strong> ${JSON.stringify(t)}`}formatHookEvent(e){const t=e.data,n=t.event_type||e.subtype||"unknown";switch(n){case"user_prompt":const s=t.prompt_text||t.prompt_preview||"";return`<strong>User Prompt:</strong> ${(s.length>80?s.substring(0,80)+"...":s)||"No prompt text"}`;case"pre_tool":const i=t.tool_name||"Unknown tool";return`<strong>Pre-Tool (${t.operation_type||"operation"}):</strong> ${i}`;case"post_tool":const o=t.tool_name||"Unknown tool";return`<strong>Post-Tool (${t.success?"success":t.status||"failed"}):</strong> ${o}${t.duration_ms?` (${t.duration_ms}ms)`:""}`;case"notification":return`<strong>Notification (${t.notification_type||"notification"}):</strong> ${t.message_preview||t.message||"No message"}`;case"stop":const r=t.reason||"unknown";return`<strong>Stop (${t.stop_type||"normal"}):</strong> ${r}`;case"subagent_stop":return`<strong>Subagent Stop (${t.agent_type||"unknown agent"}):</strong> ${t.reason||"unknown"}`;default:const a=t.hook_name||t.name||t.event_type||"Unknown";return`<strong>Hook ${e.subtype||n}:</strong> ${a}`}}formatTodoEvent(e){const t=e.data;if(t.todos&&Array.isArray(t.todos)){const e=t.todos.length;return`<strong>Todo updated:</strong> ${e} item${1!==e?"s":""}`}return`<strong>Todo:</strong> ${JSON.stringify(t)}`}formatMemoryEvent(e){const t=e.data;return`<strong>Memory ${t.operation||"unknown"}:</strong> ${t.key||"Unknown key"}`}formatLogEvent(e){const t=e.data,n=t.level||"info",s=t.message||"",i=s.length>80?s.substring(0,80)+"...":s;return`<strong>[${n.toUpperCase()}]</strong> ${i}`}formatGenericEvent(e){const t=e.data;return"string"==typeof t?t.length>100?t.substring(0,100)+"...":t:JSON.stringify(t)}formatSingleRowEventContent(e){const t=this.formatEventType(e),n=e.data||{};let s="",i="";switch(e.type){case"hook":const t=e.tool_name||n.tool_name||"Unknown",o=e.subtype||"Unknown",r=this.getHookDisplayName(o,n);i=this.getEventCategory(e),s=`${r} (${i}): ${t}`;break;case"agent":i="agent_operations",s=`${e.subagent_type||n.subagent_type||"PM"} ${e.subtype||"action"}`;break;case"todo":i="task_management",s=`TodoWrite (${n.todos?n.todos.length:0} items)`;break;case"memory":i="memory_operations",s=`${n.operation||"unknown"} ${n.key||"unknown"}`;break;case"session":i="session_management",s=`Session ${e.subtype||"unknown"}`;break;case"claude":i="claude_interactions",s=`Claude ${e.subtype||"interaction"}`;break;default:i="general",s=e.type||"Unknown Event"}return`${t} ${s}`}getHookDisplayName(e,t){const n={pre_tool:"Pre-Tool",post_tool:"Post-Tool",user_prompt:"User-Prompt",stop:"Stop",subagent_stop:"Subagent-Stop",notification:"Notification"};if(n[e])return n[e];return String(e||"unknown").replace(/_/g," ")}getEventCategory(e){const t=e.data||{},n=e.tool_name||t.tool_name||"";return["Read","Write","Edit","MultiEdit"].includes(n)?"file_operations":["Bash","grep","Glob"].includes(n)?"system_operations":"TodoWrite"===n?"task_management":"Task"===n?"agent_delegation":"stop"===e.subtype||"subagent_stop"===e.subtype?"session_control":"general"}showEventDetails(e){if(!this.filteredEvents||!Array.isArray(this.filteredEvents))return void console.warn("EventViewer: filteredEvents array is not initialized");if(e<0||e>=this.filteredEvents.length)return;this.selectedEventIndex=e;const t=this.filteredEvents[e];window.dashboard&&(window.dashboard.tabNavigation&&window.dashboard.tabNavigation.events&&(window.dashboard.tabNavigation.events.selectedIndex=e),window.dashboard.selectCard&&window.dashboard.selectCard("events",e,"event",t)),this.filteredEventElements.forEach((t,n)=>{t.classList.toggle("selected",n===e)}),document.dispatchEvent(new CustomEvent("eventSelected",{detail:{event:t,index:e}}));const n=this.filteredEventElements[e];n&&n.scrollIntoView({behavior:"smooth",block:"nearest"})}clearSelection(){this.selectedEventIndex=-1,this.filteredEventElements.forEach(e=>{e.classList.remove("selected")}),window.dashboard&&(window.dashboard.tabNavigation&&window.dashboard.tabNavigation.events&&(window.dashboard.tabNavigation.events.selectedIndex=-1),window.dashboard.clearCardSelection&&window.dashboard.clearCardSelection()),document.dispatchEvent(new CustomEvent("eventSelectionCleared"))}updateMetrics(){this.eventTypeCount={},this.errorCount=0,this.events&&Array.isArray(this.events)||(console.warn("EventViewer: events array is not initialized in updateMetrics"),this.events=[]),this.events.forEach(e=>{const t=e.type||"unknown";this.eventTypeCount[t]=(this.eventTypeCount[t]||0)+1,"log"===e.type&&e.data&&["error","critical"].includes(e.data.level)&&this.errorCount++});const e=(new Date).getMinutes();e!==this.lastMinute&&(this.lastMinute=e,this.eventsThisMinute=0);const t=new Date(Date.now()-6e4);this.eventsThisMinute=this.events.filter(e=>new Date(e.timestamp)>t).length,this.updateMetricsUI()}updateMetricsUI(){const e=document.getElementById("total-events"),t=document.getElementById("events-per-minute"),n=document.getElementById("unique-types"),s=document.getElementById("error-count");e&&(e.textContent=this.events.length),t&&(t.textContent=this.eventsThisMinute),n&&(n.textContent=Object.keys(this.eventTypeCount).length),s&&(s.textContent=this.errorCount)}exportEvents(){const e=JSON.stringify(this.filteredEvents,null,2),t=new Blob([e],{type:"application/json"}),n=URL.createObjectURL(t),s=document.createElement("a");s.href=n,s.download=`claude-mpm-events-${(new Date).toISOString().split("T")[0]}.json`,s.click(),URL.revokeObjectURL(n)}clearEvents(){this.socketClient.clearEvents(),this.selectedEventIndex=-1,this.updateDisplay()}setSessionFilter(e){this.sessionFilter=e,this.applyFilters()}getFilters(){return{search:this.searchFilter,type:this.typeFilter,session:this.sessionFilter}}getFilteredEvents(){return this.filteredEvents}getAllEvents(){return this.events}createInlineEditDiffViewer(e,t){const n=e.data||{},s=e.tool_name||n.tool_name||"";if(!["Edit","MultiEdit"].includes(s))return"";let i=[];if("Edit"===s){const t=e.tool_parameters||n.tool_parameters||{};t.old_string&&t.new_string&&i.push({old_string:t.old_string,new_string:t.new_string,file_path:t.file_path||"unknown"})}else if("MultiEdit"===s){const t=e.tool_parameters||n.tool_parameters||{};t.edits&&Array.isArray(t.edits)&&(i=t.edits.map(e=>({...e,file_path:t.file_path||"unknown"})))}if(0===i.length)return"";const o=`edit-diff-${t}`,r=i.length>1;let a="";return i.forEach((e,t)=>{const n=this.createDiffHtml(e.old_string,e.new_string);a+=`\n <div class="edit-diff-section">\n ${r?`<div class="edit-diff-header">Edit ${t+1}</div>`:""}\n <div class="diff-content">${n}</div>\n </div>\n `}),`\n <div class="inline-edit-diff-viewer">\n <div class="diff-toggle-header" onclick="eventViewer.toggleEditDiff('${o}', event)">\n <span class="diff-toggle-icon">📋</span>\n <span class="diff-toggle-text">Show ${r?i.length+" edits":"edit"}</span>\n <span class="diff-toggle-arrow">▼</span>\n </div>\n <div id="${o}" class="diff-content-container" style="display: none;">\n ${a}\n </div>\n </div>\n `}createDiffHtml(e,t){const n=e.split("\n"),s=t.split("\n");let i="",o=0,r=0;for(;o<n.length||r<s.length;){const e=o<n.length?n[o]:null,t=r<s.length?s[r]:null;null===e?(i+=`<div class="diff-line diff-added">+ ${this.escapeHtml(t)}</div>`,r++):null===t?(i+=`<div class="diff-line diff-removed">- ${this.escapeHtml(e)}</div>`,o++):e===t?(i+=`<div class="diff-line diff-unchanged"> ${this.escapeHtml(e)}</div>`,o++,r++):(i+=`<div class="diff-line diff-removed">- ${this.escapeHtml(e)}</div>`,i+=`<div class="diff-line diff-added">+ ${this.escapeHtml(t)}</div>`,o++,r++)}return`<div class="diff-container">${i}</div>`}toggleEditDiff(e,t){t.stopPropagation();const n=document.getElementById(e),s=t.currentTarget.querySelector(".diff-toggle-arrow");if(n){const e="none"!==n.style.display;n.style.display=e?"none":"block",s&&(s.textContent=e?"▼":"▲")}}escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}}window.EventViewer=e;class t{constructor(e,t){this.eventViewer=e,this.agentInference=t,this.agentEvents=[],this.filteredAgentEvents=[],this.filteredToolEvents=[],this.filteredFileEvents=[],this.selectedSessionId=null,this.fileTrackingCache=new Map,this.trackingCheckTimeout=3e4,console.log("Event processor initialized")}getFilteredEventsForTab(e){const t=this.eventViewer.events;console.log(`getFilteredEventsForTab(${e}) - using RAW events: ${t.length} total`);const n=window.sessionManager;if(n&&n.selectedSessionId){const e=n.getEventsForSession(n.selectedSessionId);return console.log(`Filtering by session ${n.selectedSessionId}: ${e.length} events`),e}return t}applyAgentsFilters(e){const t=document.getElementById("agents-search-input"),n=document.getElementById("agents-type-filter"),s=t?t.value.toLowerCase():"",i=n?n.value:"";return e.filter(e=>{if(s){if(![e.agentName||"",e.type||"",e.isImplied?"implied":"explicit"].join(" ").toLowerCase().includes(s))return!1}if(i){if(!(e.agentName||"unknown").toLowerCase().includes(i.toLowerCase()))return!1}return!0})}applyToolsFilters(e){const t=document.getElementById("tools-search-input"),n=document.getElementById("tools-type-filter"),s=t?t.value.toLowerCase():"",i=n?n.value:"";return e.filter(e=>{if(s){if(![e.tool_name||"",e.agent_type||"",e.type||"",e.subtype||""].join(" ").toLowerCase().includes(s))return!1}if(i){if((e.tool_name||"")!==i)return!1}return!0})}applyToolCallFilters(e){const t=document.getElementById("tools-search-input"),n=document.getElementById("tools-type-filter"),s=t?t.value.toLowerCase():"",i=n?n.value:"";return e.filter(([e,t])=>{if(s){if(![t.tool_name||"",t.agent_type||"","tool_call"].join(" ").toLowerCase().includes(s))return!1}if(i){if((t.tool_name||"")!==i)return!1}return!0})}applyFilesFilters(e){const t=document.getElementById("files-search-input"),n=document.getElementById("files-type-filter"),s=t?t.value.toLowerCase():"",i=n?n.value:"";return e.filter(([e,t])=>{if(this.selectedSessionId){const e=t.operations.filter(e=>e.sessionId===this.selectedSessionId);if(0===e.length)return!1;t={...t,operations:e,lastOperation:e[e.length-1]?.timestamp||t.lastOperation}}if(s){if(![e,...t.operations.map(e=>e.operation),...t.operations.map(e=>e.agent)].join(" ").toLowerCase().includes(s))return!1}if(i){if(!t.operations.map(e=>e.operation).includes(i))return!1}return!0})}extractOperation(e){if(!e)return"unknown";const t=e.toLowerCase();return t.includes("read")?"read":t.includes("write")?"write":t.includes("edit")?"edit":t.includes("create")?"create":t.includes("delete")?"delete":t.includes("move")||t.includes("rename")?"move":"other"}extractToolFromHook(e){if(!e)return"";const t=e.match(/^(?:Pre|Post)(.+)Use$/);return t?t[1]:""}extractToolFromSubtype(e){if(!e)return"";if(e.includes("_")){return e.split("_")[0]||""}return e}extractToolTarget(e,t,n){const s=t||n||{};switch(e?.toLowerCase()){case"read":case"write":case"edit":return s.file_path||s.path||"";case"bash":return s.command||"";case"grep":return s.pattern||"";case"task":return s.subagent_type||s.agent_type||"";default:const e=Object.keys(s),t=["path","file_path","command","pattern","query","target"];for(const n of t)if(s[n])return s[n];return e.length>0?`${e[0]}: ${s[e[0]]}`:""}}generateAgentHTML(e){const t=this.agentInference.getUniqueAgentInstances();return this.applyAgentsFilters(t).map((e,t)=>{const n=e.agentName,s=this.formatTimestamp(e.firstTimestamp||e.timestamp),i=e.isImplied?"implied":"explicit",o=e.totalEventCount||e.eventCount||0;return`\n <div class="event-item single-row event-agent" onclick="${`dashboard.selectCard('agents', ${t}, 'agent_instance', '${e.id}'); dashboard.showAgentInstanceDetails('${e.id}');`}">\n <span class="event-single-row-content">\n <span class="event-content-main">${`${n} (${i}, ${o} events)`}</span>\n <span class="event-timestamp">${s}</span>\n </span>\n </div>\n `}).join("")}generateToolHTML(e){return this.applyToolCallFilters(e).map(([e,t],n)=>{const s=t.tool_name||"Unknown",i=t.agent_type||"Unknown",o=this.formatTimestamp(t.timestamp);return`\n <div class="event-item single-row event-tool ${"completed"===(t.post_event?"completed":"pending")?"status-success":"status-pending"}" onclick="dashboard.selectCard('tools', ${n}, 'toolCall', '${e}'); dashboard.showToolCallDetails('${e}')">\n <span class="event-single-row-content">\n <span class="event-content-main">${`${s} (${"pm"===i.toLowerCase()?"pm":i})`}</span>\n <span class="event-timestamp">${o}</span>\n </span>\n </div>\n `}).join("")}generateFileHTML(e){return this.applyFilesFilters(e).map(([e,t],n)=>{const s=t.operations.map(e=>e.operation),i=this.formatTimestamp(t.lastOperation),o={};s.forEach(e=>{o[e]=(o[e]||0)+1});const r=Object.entries(o).map(([e,t])=>`${e}(${t})`).join(", "),a=[...new Set(t.operations.map(e=>e.agent))],l=a.length>1?`by ${a.length} agents`:`by ${a[0]||"unknown"}`;return`\n <div class="event-item single-row file-item" onclick="dashboard.selectCard('files', ${n}, 'file', '${e}'); dashboard.showFileDetails('${e}')">\n <span class="event-single-row-content">\n <span class="event-content-main">${`${this.getRelativeFilePath(e)} ${r} ${l}`}</span>\n <span class="event-timestamp">${i}</span>\n </span>\n </div>\n `}).join("")}getFileOperationIcon(e){return e.includes("write")||e.includes("create")?"📝":e.includes("edit")?"✏️":e.includes("read")?"👁️":e.includes("delete")?"🗑️":e.includes("move")?"📦":"📄"}getRelativeFilePath(e){if(!e)return"";const t=e.split("/");return t.length>3?".../"+t.slice(-2).join("/"):e}formatTimestamp(e){if(!e)return"";return new Date(e).toLocaleTimeString()}setSelectedSessionId(e){this.selectedSessionId=e}getSelectedSessionId(){return this.selectedSessionId}getUniqueToolInstances(e){return this.applyToolCallFilters(e)}getUniqueFileInstances(e){return this.applyFilesFilters(e)}async isFileTracked(e,t){const n=`${t}:${e}`,s=Date.now(),i=this.fileTrackingCache.get(n);if(i&&s-i.timestamp<this.trackingCheckTimeout)return i.is_tracked;try{const i=window.socket;return i?new Promise(o=>{const r=t=>{if(t.file_path===e){const e=t.success&&t.is_tracked;this.fileTrackingCache.set(n,{is_tracked:e,timestamp:s}),i.off("file_tracked_response",r),o(e)}};i.on("file_tracked_response",r),i.emit("check_file_tracked",{file_path:e,working_dir:t}),setTimeout(()=>{i.off("file_tracked_response",r),o(!1)},5e3)}):(console.warn("No socket connection available for git tracking check"),!1)}catch(o){return console.error("Error checking file tracking status:",o),!1}}generateGitDiffIcon(e,t,n){const s=`git-icon-${e.replace(/[^a-zA-Z0-9]/g,"-")}-${t}`,i=`\n <span id="${s}" class="git-diff-icon"\n onclick="event.stopPropagation(); showGitDiffModal('${e}', '${t}')"\n title="View git diff for this file operation"\n style="margin-left: 8px; cursor: pointer; font-size: 16px;">\n 📋\n </span>\n `;return this.isFileTracked(e,n).then(e=>{const t=document.getElementById(s);t&&(e?(t.innerHTML="📋",t.title="View git diff for this file operation",t.classList.add("tracked-file")):(t.innerHTML="📋❌",t.title="File not tracked by git - click to see details",t.classList.add("untracked-file")))}).catch(e=>{console.error("Error updating git diff icon:",e)}),i}showAgentInstanceDetails(e){const t=this.agentInference.getPMDelegations().get(e);if(!t)return void console.error("Agent instance not found:",e);console.log("Showing agent instance details for:",e,t);const n=`\n <div class="agent-instance-details">\n <h3>Agent Instance: ${t.agentName}</h3>\n <p><strong>Type:</strong> ${t.isImplied?"Implied PM Delegation":"Explicit PM Delegation"}</p>\n <p><strong>Start Time:</strong> ${this.formatTimestamp(t.timestamp)}</p>\n <p><strong>Event Count:</strong> ${t.agentEvents.length}</p>\n <p><strong>Session:</strong> ${t.sessionId}</p>\n ${t.pmCall?`<p><strong>PM Call:</strong> Task delegation to ${t.agentName}</p>`:"<p><strong>Note:</strong> Implied delegation (no explicit PM call found)</p>"}\n </div>\n `;console.log("Agent instance details HTML:",n)}}export{e as E,t as a};
|
|
2
2
|
//# sourceMappingURL=event-viewer.js.map
|