claude-mpm 5.1.9__py3-none-any.whl → 5.4.48__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +4 -0
- claude_mpm/agents/BASE_AGENT.md +164 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
- claude_mpm/agents/MEMORY.md +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +843 -900
- claude_mpm/agents/WORKFLOW.md +5 -254
- claude_mpm/agents/agent_loader.py +13 -44
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/frontmatter_validator.py +2 -2
- claude_mpm/agents/templates/circuit-breakers.md +138 -1
- claude_mpm/cli/__main__.py +4 -0
- claude_mpm/cli/chrome_devtools_installer.py +175 -0
- claude_mpm/cli/commands/agent_state_manager.py +18 -27
- claude_mpm/cli/commands/agents.py +9 -40
- claude_mpm/cli/commands/auto_configure.py +210 -25
- claude_mpm/cli/commands/config.py +88 -2
- claude_mpm/cli/commands/configure.py +1098 -159
- claude_mpm/cli/commands/configure_agent_display.py +25 -6
- claude_mpm/cli/commands/mpm_init/core.py +225 -46
- claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
- claude_mpm/cli/commands/postmortem.py +1 -1
- claude_mpm/cli/commands/profile.py +277 -0
- claude_mpm/cli/commands/skills.py +218 -197
- claude_mpm/cli/commands/summarize.py +413 -0
- claude_mpm/cli/executor.py +21 -3
- claude_mpm/cli/interactive/agent_wizard.py +2 -2
- claude_mpm/cli/parsers/agents_parser.py +0 -9
- claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
- claude_mpm/cli/parsers/base_parser.py +12 -0
- claude_mpm/cli/parsers/config_parser.py +153 -83
- claude_mpm/cli/parsers/profile_parser.py +148 -0
- claude_mpm/cli/parsers/skills_parser.py +0 -5
- claude_mpm/cli/startup.py +876 -149
- claude_mpm/commands/mpm-config.md +28 -0
- claude_mpm/commands/mpm-doctor.md +9 -22
- claude_mpm/commands/mpm-help.md +5 -287
- claude_mpm/commands/mpm-init.md +81 -507
- claude_mpm/commands/mpm-monitor.md +15 -402
- claude_mpm/commands/mpm-organize.md +120 -0
- claude_mpm/commands/mpm-postmortem.md +6 -108
- claude_mpm/commands/mpm-session-resume.md +12 -363
- claude_mpm/commands/mpm-status.md +5 -69
- claude_mpm/commands/mpm-ticket-view.md +52 -495
- claude_mpm/commands/mpm-version.md +5 -107
- claude_mpm/config/agent_sources.py +27 -0
- claude_mpm/core/config.py +2 -4
- claude_mpm/core/framework/formatters/content_formatter.py +3 -13
- claude_mpm/core/framework/loaders/agent_loader.py +8 -5
- claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
- claude_mpm/core/framework_loader.py +4 -2
- claude_mpm/core/logger.py +13 -0
- claude_mpm/core/optimized_startup.py +59 -0
- claude_mpm/core/shared/config_loader.py +1 -1
- claude_mpm/core/socketio_pool.py +3 -3
- claude_mpm/core/unified_agent_registry.py +5 -15
- claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
- claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
- claude_mpm/dashboard/static/svelte-build/index.html +36 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
- claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
- claude_mpm/hooks/claude_hooks/installer.py +33 -10
- claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
- claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +30 -6
- claude_mpm/hooks/kuzu_memory_hook.py +5 -5
- claude_mpm/hooks/memory_integration_hook.py +46 -1
- claude_mpm/init.py +63 -19
- claude_mpm/models/git_repository.py +3 -3
- claude_mpm/scripts/claude-hook-handler.sh +58 -18
- claude_mpm/scripts/launch_monitor.py +93 -13
- claude_mpm/services/agents/agent_builder.py +3 -3
- claude_mpm/services/agents/agent_recommendation_service.py +278 -0
- claude_mpm/services/agents/agent_review_service.py +280 -0
- claude_mpm/services/agents/cache_git_manager.py +6 -6
- claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
- claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -5
- claude_mpm/services/agents/deployment/agent_format_converter.py +23 -13
- claude_mpm/services/agents/deployment/agent_template_builder.py +32 -20
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
- claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
- claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +247 -35
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +392 -87
- claude_mpm/services/agents/git_source_manager.py +53 -4
- claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
- claude_mpm/services/agents/recommender.py +5 -3
- claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
- claude_mpm/services/agents/sources/git_source_sync_service.py +120 -7
- claude_mpm/services/agents/startup_sync.py +22 -2
- claude_mpm/services/agents/toolchain_detector.py +10 -6
- claude_mpm/services/analysis/__init__.py +11 -1
- claude_mpm/services/analysis/clone_detector.py +1030 -0
- claude_mpm/services/command_deployment_service.py +81 -10
- claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
- claude_mpm/services/event_bus/config.py +3 -1
- claude_mpm/services/git/git_operations_service.py +101 -16
- claude_mpm/services/monitor/daemon.py +9 -2
- claude_mpm/services/monitor/daemon_manager.py +39 -3
- claude_mpm/services/monitor/management/lifecycle.py +8 -1
- claude_mpm/services/monitor/server.py +698 -22
- claude_mpm/services/pm_skills_deployer.py +711 -0
- claude_mpm/services/profile_manager.py +331 -0
- claude_mpm/services/self_upgrade_service.py +120 -12
- claude_mpm/services/skills/__init__.py +3 -0
- claude_mpm/services/skills/git_skill_source_manager.py +130 -2
- claude_mpm/services/skills/selective_skill_deployer.py +704 -0
- claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
- claude_mpm/services/skills_deployer.py +127 -9
- claude_mpm/services/socketio/dashboard_server.py +1 -0
- claude_mpm/services/socketio/event_normalizer.py +51 -6
- claude_mpm/services/socketio/server/core.py +386 -108
- claude_mpm/services/version_control/git_operations.py +103 -0
- claude_mpm/skills/skill_manager.py +92 -3
- claude_mpm/utils/agent_dependency_loader.py +14 -2
- claude_mpm/utils/agent_filters.py +17 -44
- claude_mpm/utils/migration.py +4 -4
- claude_mpm/utils/robust_installer.py +47 -3
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/METADATA +53 -87
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/RECORD +157 -197
- claude_mpm-5.4.48.dist-info/entry_points.txt +5 -0
- claude_mpm-5.4.48.dist-info/licenses/LICENSE +94 -0
- claude_mpm-5.4.48.dist-info/licenses/LICENSE-FAQ.md +153 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
- claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
- claude_mpm/agents/BASE_OPS.md +0 -219
- claude_mpm/agents/BASE_PM.md +0 -480
- claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
- claude_mpm/agents/BASE_QA.md +0 -167
- claude_mpm/agents/BASE_RESEARCH.md +0 -53
- claude_mpm/agents/base_agent_loader.py +0 -601
- claude_mpm/cli/commands/agents_detect.py +0 -380
- claude_mpm/cli/commands/agents_recommend.py +0 -309
- claude_mpm/cli/ticket_cli.py +0 -35
- claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
- claude_mpm/commands/mpm-agents-detect.md +0 -177
- claude_mpm/commands/mpm-agents-list.md +0 -131
- claude_mpm/commands/mpm-agents-recommend.md +0 -223
- claude_mpm/commands/mpm-config-view.md +0 -150
- claude_mpm/commands/mpm-ticket-organize.md +0 -304
- claude_mpm/dashboard/analysis_runner.py +0 -455
- claude_mpm/dashboard/index.html +0 -13
- claude_mpm/dashboard/open_dashboard.py +0 -66
- claude_mpm/dashboard/static/css/activity.css +0 -1958
- claude_mpm/dashboard/static/css/connection-status.css +0 -370
- claude_mpm/dashboard/static/css/dashboard.css +0 -4701
- claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
- claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
- claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
- claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
- claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
- claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
- claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
- claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
- claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
- claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
- claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
- claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
- claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
- claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
- claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
- claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
- claude_mpm/dashboard/static/js/connection-manager.js +0 -536
- claude_mpm/dashboard/static/js/dashboard.js +0 -1914
- claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
- claude_mpm/dashboard/static/js/socket-client.js +0 -1474
- claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
- claude_mpm/dashboard/static/socket.io.min.js +0 -7
- claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
- claude_mpm/dashboard/templates/code_simple.html +0 -153
- claude_mpm/dashboard/templates/index.html +0 -606
- claude_mpm/dashboard/test_dashboard.html +0 -372
- claude_mpm/scripts/mcp_server.py +0 -75
- claude_mpm/scripts/mcp_wrapper.py +0 -39
- claude_mpm/services/mcp_gateway/__init__.py +0 -159
- claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
- claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
- claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
- claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
- claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
- claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
- claude_mpm/services/mcp_gateway/core/base.py +0 -312
- claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
- claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
- claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
- claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
- claude_mpm/services/mcp_gateway/main.py +0 -589
- claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
- claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
- claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
- claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
- claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
- claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
- claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
- claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
- claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
- claude_mpm-5.1.9.dist-info/entry_points.txt +0 -10
- claude_mpm-5.1.9.dist-info/licenses/LICENSE +0 -21
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/WHEEL +0 -0
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/top_level.txt +0 -0
|
@@ -304,6 +304,10 @@ class ClaudeHookHandler:
|
|
|
304
304
|
# Perform periodic cleanup if needed
|
|
305
305
|
if self.state_manager.increment_events_processed():
|
|
306
306
|
self.state_manager.cleanup_old_entries()
|
|
307
|
+
# Also cleanup old correlation files
|
|
308
|
+
from .correlation_manager import CorrelationManager
|
|
309
|
+
|
|
310
|
+
CorrelationManager.cleanup_old()
|
|
307
311
|
if DEBUG:
|
|
308
312
|
print(
|
|
309
313
|
f"🧹 Performed cleanup after {self.state_manager.events_processed} events",
|
|
@@ -390,6 +394,8 @@ class ClaudeHookHandler:
|
|
|
390
394
|
Returns:
|
|
391
395
|
Modified input for PreToolUse events (v2.0.30+), None otherwise
|
|
392
396
|
"""
|
|
397
|
+
import time
|
|
398
|
+
|
|
393
399
|
# Try multiple field names for compatibility
|
|
394
400
|
hook_type = (
|
|
395
401
|
event.get("hook_event_name")
|
|
@@ -413,21 +419,48 @@ class ClaudeHookHandler:
|
|
|
413
419
|
"Notification": self.event_handlers.handle_notification_fast,
|
|
414
420
|
"Stop": self.event_handlers.handle_stop_fast,
|
|
415
421
|
"SubagentStop": self.event_handlers.handle_subagent_stop_fast,
|
|
422
|
+
"SubagentStart": self.event_handlers.handle_session_start_fast,
|
|
423
|
+
"SessionStart": self.event_handlers.handle_session_start_fast,
|
|
416
424
|
"AssistantResponse": self.event_handlers.handle_assistant_response,
|
|
417
425
|
}
|
|
418
426
|
|
|
419
427
|
# Call appropriate handler if exists
|
|
420
428
|
handler = event_handlers.get(hook_type)
|
|
421
429
|
if handler:
|
|
430
|
+
# Track execution timing for hook emission
|
|
431
|
+
start_time = time.time()
|
|
432
|
+
success = False
|
|
433
|
+
error_message = None
|
|
434
|
+
result = None
|
|
435
|
+
|
|
422
436
|
try:
|
|
423
437
|
# Handlers can optionally return modified input
|
|
424
438
|
result = handler(event)
|
|
439
|
+
success = True
|
|
425
440
|
# Only PreToolUse handlers should return modified input
|
|
426
441
|
if hook_type == "PreToolUse" and result is not None:
|
|
427
|
-
|
|
442
|
+
return_value = result
|
|
443
|
+
else:
|
|
444
|
+
return_value = None
|
|
428
445
|
except Exception as e:
|
|
446
|
+
error_message = str(e)
|
|
447
|
+
return_value = None
|
|
429
448
|
if DEBUG:
|
|
430
449
|
print(f"Error handling {hook_type}: {e}", file=sys.stderr)
|
|
450
|
+
finally:
|
|
451
|
+
# Calculate duration
|
|
452
|
+
duration_ms = int((time.time() - start_time) * 1000)
|
|
453
|
+
|
|
454
|
+
# Emit hook execution event
|
|
455
|
+
self._emit_hook_execution_event(
|
|
456
|
+
hook_type=hook_type,
|
|
457
|
+
event=event,
|
|
458
|
+
success=success,
|
|
459
|
+
duration_ms=duration_ms,
|
|
460
|
+
error_message=error_message,
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
return return_value
|
|
431
464
|
|
|
432
465
|
return None
|
|
433
466
|
|
|
@@ -472,6 +505,127 @@ class ClaudeHookHandler:
|
|
|
472
505
|
"""Generate event key through duplicate detector (backward compatibility)."""
|
|
473
506
|
return self.duplicate_detector.generate_event_key(event)
|
|
474
507
|
|
|
508
|
+
def _emit_hook_execution_event(
|
|
509
|
+
self,
|
|
510
|
+
hook_type: str,
|
|
511
|
+
event: dict,
|
|
512
|
+
success: bool,
|
|
513
|
+
duration_ms: int,
|
|
514
|
+
error_message: Optional[str] = None,
|
|
515
|
+
):
|
|
516
|
+
"""Emit a structured JSON event for hook execution.
|
|
517
|
+
|
|
518
|
+
This emits a normalized event following the claude_event schema to provide
|
|
519
|
+
visibility into hook processing, timing, and success/failure status.
|
|
520
|
+
|
|
521
|
+
Args:
|
|
522
|
+
hook_type: The type of hook that executed (e.g., "UserPromptSubmit", "PreToolUse")
|
|
523
|
+
event: The original hook event data
|
|
524
|
+
success: Whether the hook executed successfully
|
|
525
|
+
duration_ms: How long the hook took to execute in milliseconds
|
|
526
|
+
error_message: Optional error message if the hook failed
|
|
527
|
+
"""
|
|
528
|
+
# Generate a human-readable summary based on hook type
|
|
529
|
+
summary = self._generate_hook_summary(hook_type, event, success)
|
|
530
|
+
|
|
531
|
+
# Extract common fields
|
|
532
|
+
session_id = event.get("session_id", "")
|
|
533
|
+
working_dir = event.get("cwd", "")
|
|
534
|
+
|
|
535
|
+
# Build hook execution data
|
|
536
|
+
hook_data = {
|
|
537
|
+
"hook_name": hook_type,
|
|
538
|
+
"hook_type": hook_type,
|
|
539
|
+
"session_id": session_id,
|
|
540
|
+
"working_directory": working_dir,
|
|
541
|
+
"success": success,
|
|
542
|
+
"duration_ms": duration_ms,
|
|
543
|
+
"result_summary": summary,
|
|
544
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
# Add error information if present
|
|
548
|
+
if error_message:
|
|
549
|
+
hook_data["error_message"] = error_message
|
|
550
|
+
|
|
551
|
+
# Add hook-specific context
|
|
552
|
+
if hook_type == "PreToolUse":
|
|
553
|
+
hook_data["tool_name"] = event.get("tool_name", "")
|
|
554
|
+
elif hook_type == "PostToolUse":
|
|
555
|
+
hook_data["tool_name"] = event.get("tool_name", "")
|
|
556
|
+
hook_data["exit_code"] = event.get("exit_code", 0)
|
|
557
|
+
elif hook_type == "UserPromptSubmit":
|
|
558
|
+
prompt = event.get("prompt", "")
|
|
559
|
+
hook_data["prompt_preview"] = prompt[:100] if len(prompt) > 100 else prompt
|
|
560
|
+
hook_data["prompt_length"] = len(prompt)
|
|
561
|
+
elif hook_type == "SubagentStop":
|
|
562
|
+
hook_data["agent_type"] = event.get("agent_type", "unknown")
|
|
563
|
+
hook_data["reason"] = event.get("reason", "unknown")
|
|
564
|
+
|
|
565
|
+
# Emit through connection manager with proper structure
|
|
566
|
+
# This uses the existing event infrastructure
|
|
567
|
+
self._emit_socketio_event("", "hook_execution", hook_data)
|
|
568
|
+
|
|
569
|
+
if DEBUG:
|
|
570
|
+
print(
|
|
571
|
+
f"📊 Hook execution event: {hook_type} - {duration_ms}ms - {'✅' if success else '❌'}",
|
|
572
|
+
file=sys.stderr,
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
def _generate_hook_summary(self, hook_type: str, event: dict, success: bool) -> str:
|
|
576
|
+
"""Generate a human-readable summary of what the hook did.
|
|
577
|
+
|
|
578
|
+
Args:
|
|
579
|
+
hook_type: The type of hook
|
|
580
|
+
event: The hook event data
|
|
581
|
+
success: Whether the hook executed successfully
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
A brief description of what happened
|
|
585
|
+
"""
|
|
586
|
+
if not success:
|
|
587
|
+
return f"Hook {hook_type} failed during processing"
|
|
588
|
+
|
|
589
|
+
# Generate hook-specific summaries
|
|
590
|
+
if hook_type == "UserPromptSubmit":
|
|
591
|
+
prompt = event.get("prompt", "")
|
|
592
|
+
if prompt.startswith("/"):
|
|
593
|
+
return f"Processed command: {prompt.split()[0]}"
|
|
594
|
+
return f"Processed user prompt ({len(prompt)} chars)"
|
|
595
|
+
|
|
596
|
+
if hook_type == "PreToolUse":
|
|
597
|
+
tool_name = event.get("tool_name", "unknown")
|
|
598
|
+
return f"Pre-processing tool call: {tool_name}"
|
|
599
|
+
|
|
600
|
+
if hook_type == "PostToolUse":
|
|
601
|
+
tool_name = event.get("tool_name", "unknown")
|
|
602
|
+
exit_code = event.get("exit_code", 0)
|
|
603
|
+
status = "success" if exit_code == 0 else "failed"
|
|
604
|
+
return f"Completed tool call: {tool_name} ({status})"
|
|
605
|
+
|
|
606
|
+
if hook_type == "SubagentStop":
|
|
607
|
+
agent_type = event.get("agent_type", "unknown")
|
|
608
|
+
reason = event.get("reason", "unknown")
|
|
609
|
+
return f"Subagent {agent_type} stopped: {reason}"
|
|
610
|
+
|
|
611
|
+
if hook_type == "SessionStart":
|
|
612
|
+
return "New session started"
|
|
613
|
+
|
|
614
|
+
if hook_type == "Stop":
|
|
615
|
+
reason = event.get("reason", "unknown")
|
|
616
|
+
return f"Session stopped: {reason}"
|
|
617
|
+
|
|
618
|
+
if hook_type == "Notification":
|
|
619
|
+
notification_type = event.get("notification_type", "unknown")
|
|
620
|
+
return f"Notification received: {notification_type}"
|
|
621
|
+
|
|
622
|
+
if hook_type == "AssistantResponse":
|
|
623
|
+
response_len = len(event.get("response", ""))
|
|
624
|
+
return f"Assistant response generated ({response_len} chars)"
|
|
625
|
+
|
|
626
|
+
# Default summary
|
|
627
|
+
return f"Hook {hook_type} processed successfully"
|
|
628
|
+
|
|
475
629
|
def __del__(self):
|
|
476
630
|
"""Cleanup on handler destruction."""
|
|
477
631
|
# Clean up connection manager if it exists
|
|
@@ -202,8 +202,9 @@ main "$@"
|
|
|
202
202
|
self.hooks_dir = self.claude_dir / "hooks" # Kept for backward compatibility
|
|
203
203
|
# Use settings.json for hooks (Claude Code reads from this file)
|
|
204
204
|
self.settings_file = self.claude_dir / "settings.json"
|
|
205
|
-
#
|
|
206
|
-
|
|
205
|
+
# There is no legacy settings file - this was a bug where both pointed to same file
|
|
206
|
+
# Setting to None to disable cleanup that was deleting freshly installed hooks
|
|
207
|
+
self.old_settings_file = None
|
|
207
208
|
self._claude_version: Optional[str] = None
|
|
208
209
|
self._hook_script_path: Optional[Path] = None
|
|
209
210
|
|
|
@@ -462,7 +463,9 @@ main "$@"
|
|
|
462
463
|
|
|
463
464
|
def _cleanup_old_settings(self) -> None:
|
|
464
465
|
"""Remove hooks from old settings.json file if present."""
|
|
465
|
-
|
|
466
|
+
# No-op: old_settings_file was pointing to same file as settings_file (bug)
|
|
467
|
+
# This was causing freshly installed hooks to be immediately deleted
|
|
468
|
+
if self.old_settings_file is None or not self.old_settings_file.exists():
|
|
466
469
|
return
|
|
467
470
|
|
|
468
471
|
try:
|
|
@@ -518,15 +521,31 @@ main "$@"
|
|
|
518
521
|
}
|
|
519
522
|
]
|
|
520
523
|
|
|
521
|
-
#
|
|
522
|
-
|
|
523
|
-
for event_type in
|
|
524
|
+
# Simple events (no subtypes, no matcher needed)
|
|
525
|
+
simple_events = ["Stop", "SubagentStop", "SubagentStart"]
|
|
526
|
+
for event_type in simple_events:
|
|
524
527
|
settings["hooks"][event_type] = [
|
|
525
528
|
{
|
|
526
529
|
"hooks": [hook_command],
|
|
527
530
|
}
|
|
528
531
|
]
|
|
529
532
|
|
|
533
|
+
# SessionStart needs matcher for subtypes (startup, resume)
|
|
534
|
+
settings["hooks"]["SessionStart"] = [
|
|
535
|
+
{
|
|
536
|
+
"matcher": "*", # Match all SessionStart subtypes
|
|
537
|
+
"hooks": [hook_command],
|
|
538
|
+
}
|
|
539
|
+
]
|
|
540
|
+
|
|
541
|
+
# UserPromptSubmit needs matcher for potential subtypes
|
|
542
|
+
settings["hooks"]["UserPromptSubmit"] = [
|
|
543
|
+
{
|
|
544
|
+
"matcher": "*",
|
|
545
|
+
"hooks": [hook_command],
|
|
546
|
+
}
|
|
547
|
+
]
|
|
548
|
+
|
|
530
549
|
# Write settings to settings.json
|
|
531
550
|
with self.settings_file.open("w") as f:
|
|
532
551
|
json.dump(settings, f, indent=2)
|
|
@@ -650,9 +669,13 @@ main "$@"
|
|
|
650
669
|
old_script.unlink()
|
|
651
670
|
self.logger.info(f"Removed old deployed script: {old_script}")
|
|
652
671
|
|
|
653
|
-
# Remove from Claude settings
|
|
654
|
-
|
|
655
|
-
|
|
672
|
+
# Remove from Claude settings
|
|
673
|
+
settings_paths = [self.settings_file]
|
|
674
|
+
if self.old_settings_file is not None:
|
|
675
|
+
settings_paths.append(self.old_settings_file)
|
|
676
|
+
|
|
677
|
+
for settings_path in settings_paths:
|
|
678
|
+
if settings_path and settings_path.exists():
|
|
656
679
|
with settings_path.open() as f:
|
|
657
680
|
settings = json.load(f)
|
|
658
681
|
|
|
@@ -763,7 +786,7 @@ main "$@"
|
|
|
763
786
|
pass
|
|
764
787
|
|
|
765
788
|
# Also check old settings file
|
|
766
|
-
if self.old_settings_file.exists():
|
|
789
|
+
if self.old_settings_file is not None and self.old_settings_file.exists():
|
|
767
790
|
try:
|
|
768
791
|
with self.old_settings_file.open() as f:
|
|
769
792
|
old_settings = json.load(f)
|
|
@@ -6,18 +6,35 @@ including pre and post delegation hooks.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
|
+
import os
|
|
9
10
|
import sys
|
|
10
11
|
|
|
11
|
-
#
|
|
12
|
+
# Install-type-aware logging configuration BEFORE kuzu-memory imports
|
|
12
13
|
# This overrides kuzu-memory's WARNING-level basicConfig (fixes 1M-445)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
# but respects production install silence
|
|
15
|
+
try:
|
|
16
|
+
from claude_mpm.core.unified_paths import DeploymentContext, PathContext
|
|
17
|
+
|
|
18
|
+
context = PathContext.detect_deployment_context()
|
|
19
|
+
|
|
20
|
+
# Only configure verbose logging for development/editable installs
|
|
21
|
+
# Production installs remain silent by default
|
|
22
|
+
if context in (DeploymentContext.DEVELOPMENT, DeploymentContext.EDITABLE_INSTALL):
|
|
23
|
+
logging.basicConfig(
|
|
24
|
+
level=logging.INFO,
|
|
25
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
26
|
+
force=True, # Python 3.8+ - reconfigures root logger
|
|
27
|
+
stream=sys.stderr,
|
|
28
|
+
)
|
|
29
|
+
except ImportError:
|
|
30
|
+
# Fallback: if unified_paths not available, configure logging
|
|
31
|
+
# This maintains backward compatibility
|
|
32
|
+
logging.basicConfig(
|
|
33
|
+
level=logging.INFO,
|
|
34
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
35
|
+
force=True,
|
|
36
|
+
stream=sys.stderr,
|
|
37
|
+
)
|
|
21
38
|
from datetime import datetime, timezone
|
|
22
39
|
from typing import Optional
|
|
23
40
|
|
|
@@ -18,8 +18,7 @@ DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
|
|
|
18
18
|
|
|
19
19
|
# Response tracking integration
|
|
20
20
|
# NOTE: ResponseTracker import moved to _initialize_response_tracking() for lazy loading
|
|
21
|
-
# This prevents unnecessary import of
|
|
22
|
-
# when hooks don't need response tracking
|
|
21
|
+
# This prevents unnecessary import of heavy dependencies when hooks don't need response tracking
|
|
23
22
|
RESPONSE_TRACKING_AVAILABLE = (
|
|
24
23
|
True # Assume available, will check on actual initialization
|
|
25
24
|
)
|
|
@@ -51,7 +50,7 @@ class ResponseTrackingManager:
|
|
|
51
50
|
response tracking without code changes.
|
|
52
51
|
|
|
53
52
|
NOTE: ResponseTracker is imported lazily here to avoid loading
|
|
54
|
-
|
|
53
|
+
heavy dependencies unless actually needed.
|
|
55
54
|
"""
|
|
56
55
|
try:
|
|
57
56
|
# Lazy import of ResponseTracker to avoid unnecessary dependency loading
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -58,7 +58,7 @@ except ImportError:
|
|
|
58
58
|
(),
|
|
59
59
|
{
|
|
60
60
|
"to_dict": lambda: {
|
|
61
|
-
"event": "
|
|
61
|
+
"event": "mpm_event",
|
|
62
62
|
"type": event_data.get("type", "unknown"),
|
|
63
63
|
"subtype": event_data.get("subtype", "generic"),
|
|
64
64
|
"timestamp": event_data.get(
|
|
@@ -115,14 +115,38 @@ class ConnectionManagerService:
|
|
|
115
115
|
- Fallback: HTTP POST for reliability when direct connection fails
|
|
116
116
|
- Eliminates duplicate events from multiple emission paths
|
|
117
117
|
"""
|
|
118
|
+
# Extract tool_call_id from data if present for correlation
|
|
119
|
+
tool_call_id = data.get("tool_call_id")
|
|
120
|
+
|
|
118
121
|
# Create event data for normalization
|
|
122
|
+
# Extract session_id (try both camelCase and snake_case)
|
|
123
|
+
session_id = data.get("session_id") or data.get("sessionId")
|
|
124
|
+
|
|
125
|
+
# Extract working directory for project identification
|
|
126
|
+
# Try multiple field names for maximum compatibility
|
|
127
|
+
cwd = (
|
|
128
|
+
data.get("cwd")
|
|
129
|
+
or data.get("working_directory")
|
|
130
|
+
or data.get("workingDirectory")
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# For hook_execution events, extract the actual hook type from data
|
|
134
|
+
# Otherwise use "hook" as the type
|
|
135
|
+
if event == "hook_execution":
|
|
136
|
+
hook_type = data.get("hook_type", "unknown")
|
|
137
|
+
event_type = hook_type
|
|
138
|
+
else:
|
|
139
|
+
event_type = "hook"
|
|
140
|
+
|
|
119
141
|
raw_event = {
|
|
120
|
-
"type": "hook"
|
|
121
|
-
"subtype": event, # e.g., "user_prompt", "pre_tool", "subagent_stop"
|
|
142
|
+
"type": event_type, # Use actual hook type for hook_execution, "hook" otherwise
|
|
143
|
+
"subtype": event, # e.g., "user_prompt", "pre_tool", "subagent_stop", "execution"
|
|
122
144
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
123
145
|
"data": data,
|
|
124
|
-
"source": "
|
|
125
|
-
"session_id":
|
|
146
|
+
"source": "mpm_hook", # Identify the source as mpm_hook
|
|
147
|
+
"session_id": session_id, # Include session if available (supports both naming conventions)
|
|
148
|
+
"cwd": cwd, # Add working directory at top level for easy frontend access
|
|
149
|
+
"correlation_id": tool_call_id, # Set from tool_call_id for event correlation
|
|
126
150
|
}
|
|
127
151
|
|
|
128
152
|
# Normalize the event using EventNormalizer for consistent schema
|
|
@@ -150,7 +174,7 @@ class ConnectionManagerService:
|
|
|
150
174
|
if self.connection_pool:
|
|
151
175
|
try:
|
|
152
176
|
# Emit to Socket.IO server directly
|
|
153
|
-
self.connection_pool.emit("
|
|
177
|
+
self.connection_pool.emit("mpm_event", claude_event_data)
|
|
154
178
|
if DEBUG:
|
|
155
179
|
print(f"✅ Emitted via connection pool: {event}", file=sys.stderr)
|
|
156
180
|
return # Success - no need for fallback
|
|
@@ -13,9 +13,9 @@ for structured memory storage with semantic search capabilities.
|
|
|
13
13
|
DESIGN DECISIONS:
|
|
14
14
|
- Priority 10 for early execution to enrich prompts before other hooks
|
|
15
15
|
- Uses subprocess to call kuzu-memory directly for maximum compatibility
|
|
16
|
-
- Graceful degradation if kuzu-memory is not
|
|
16
|
+
- Graceful degradation if kuzu-memory is not installed
|
|
17
17
|
- Automatic extraction and storage of important information
|
|
18
|
-
- kuzu-memory
|
|
18
|
+
- kuzu-memory is an OPTIONAL dependency (install with: pip install claude-mpm[memory])
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
import json
|
|
@@ -51,9 +51,9 @@ class KuzuMemoryHook(SubmitHook):
|
|
|
51
51
|
self.enabled = self.kuzu_memory_cmd is not None
|
|
52
52
|
|
|
53
53
|
if not self.enabled:
|
|
54
|
-
logger.
|
|
55
|
-
"Kuzu-memory not found
|
|
56
|
-
"
|
|
54
|
+
logger.debug(
|
|
55
|
+
"Kuzu-memory not found. Graph-based memory disabled. "
|
|
56
|
+
"To enable: pip install claude-mpm[memory] (requires cmake)"
|
|
57
57
|
)
|
|
58
58
|
else:
|
|
59
59
|
logger.info(f"Kuzu-memory integration enabled: {self.kuzu_memory_cmd}")
|
|
@@ -13,6 +13,7 @@ agent outputs because:
|
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
import re
|
|
16
|
+
from datetime import datetime
|
|
16
17
|
from typing import Dict, List
|
|
17
18
|
|
|
18
19
|
from claude_mpm.core.config import Config
|
|
@@ -47,6 +48,16 @@ except ImportError as e:
|
|
|
47
48
|
SOCKETIO_AVAILABLE = False
|
|
48
49
|
get_socketio_server = None
|
|
49
50
|
|
|
51
|
+
# Try to import event bus with fallback handling
|
|
52
|
+
try:
|
|
53
|
+
from claude_mpm.services.event_bus.event_bus import EventBus
|
|
54
|
+
|
|
55
|
+
EVENT_BUS_AVAILABLE = True
|
|
56
|
+
except ImportError as e:
|
|
57
|
+
logger.debug(f"EventBus not available: {e}")
|
|
58
|
+
EVENT_BUS_AVAILABLE = False
|
|
59
|
+
EventBus = None
|
|
60
|
+
|
|
50
61
|
|
|
51
62
|
class MemoryPreDelegationHook(PreDelegationHook):
|
|
52
63
|
"""Inject agent memory into delegation context.
|
|
@@ -83,6 +94,16 @@ class MemoryPreDelegationHook(PreDelegationHook):
|
|
|
83
94
|
logger.info("Memory manager not available - hook will be inactive")
|
|
84
95
|
self.memory_manager = None
|
|
85
96
|
|
|
97
|
+
# Initialize event bus for observability
|
|
98
|
+
if EVENT_BUS_AVAILABLE and EventBus:
|
|
99
|
+
try:
|
|
100
|
+
self.event_bus = EventBus.get_instance()
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logger.debug(f"Failed to get EventBus instance: {e}")
|
|
103
|
+
self.event_bus = None
|
|
104
|
+
else:
|
|
105
|
+
self.event_bus = None
|
|
106
|
+
|
|
86
107
|
def execute(self, context: HookContext) -> HookResult:
|
|
87
108
|
"""Add agent memory to delegation context.
|
|
88
109
|
|
|
@@ -137,7 +158,31 @@ INSTRUCTIONS: Review your memory above before proceeding. Apply learned patterns
|
|
|
137
158
|
|
|
138
159
|
logger.info(f"Injected memory for agent '{agent_id}'")
|
|
139
160
|
|
|
140
|
-
#
|
|
161
|
+
# Calculate memory size for observability
|
|
162
|
+
memory_size = len(memory_content)
|
|
163
|
+
|
|
164
|
+
# Emit event bus event for observability
|
|
165
|
+
if self.event_bus:
|
|
166
|
+
try:
|
|
167
|
+
# Determine memory source (project or user level)
|
|
168
|
+
# This is inferred from the memory manager's behavior
|
|
169
|
+
memory_source = (
|
|
170
|
+
"runtime" # Runtime loading from memory manager
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
self.event_bus.publish(
|
|
174
|
+
"agent.memory.loaded",
|
|
175
|
+
{
|
|
176
|
+
"agent_id": agent_id,
|
|
177
|
+
"memory_source": memory_source,
|
|
178
|
+
"memory_size": memory_size,
|
|
179
|
+
"timestamp": datetime.now(datetime.UTC).isoformat(),
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
except Exception as event_error:
|
|
183
|
+
logger.debug(f"EventBus publish failed: {event_error}")
|
|
184
|
+
|
|
185
|
+
# Emit Socket.IO event for memory injected (legacy compatibility)
|
|
141
186
|
try:
|
|
142
187
|
socketio_server = get_socketio_server()
|
|
143
188
|
# Calculate size of injected content
|
claude_mpm/init.py
CHANGED
|
@@ -141,25 +141,6 @@ class ProjectInitializer:
|
|
|
141
141
|
if not gitignore.exists():
|
|
142
142
|
gitignore.write_text("logs/\n*.log\n*.pyc\n__pycache__/\n")
|
|
143
143
|
|
|
144
|
-
# Also ensure MCP directories are in main project .gitignore
|
|
145
|
-
try:
|
|
146
|
-
from claude_mpm.services.project.project_organizer import (
|
|
147
|
-
ProjectOrganizer,
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
# Check if we're in a git repository
|
|
151
|
-
if (project_root / ".git").exists():
|
|
152
|
-
organizer = ProjectOrganizer(project_root)
|
|
153
|
-
# This will add MCP directories and other standard patterns
|
|
154
|
-
organizer.update_gitignore()
|
|
155
|
-
self.logger.debug(
|
|
156
|
-
"Updated project .gitignore with MCP and standard patterns"
|
|
157
|
-
)
|
|
158
|
-
except Exception as e:
|
|
159
|
-
self.logger.debug(
|
|
160
|
-
f"Could not update project gitignore with MCP patterns: {e}"
|
|
161
|
-
)
|
|
162
|
-
|
|
163
144
|
# Log successful creation with details
|
|
164
145
|
self.logger.info(f"Initialized project directory at {self.project_dir}")
|
|
165
146
|
self.logger.debug("Created directories: agents, config, responses, logs")
|
|
@@ -182,6 +163,9 @@ class ProjectInitializer:
|
|
|
182
163
|
f"✓ Found {agent_count} project agent(s) in .claude-mpm/agents/"
|
|
183
164
|
)
|
|
184
165
|
|
|
166
|
+
# Verify and deploy PM skills (non-blocking)
|
|
167
|
+
self._verify_and_deploy_pm_skills(project_root, is_mcp_mode)
|
|
168
|
+
|
|
185
169
|
return True
|
|
186
170
|
|
|
187
171
|
except Exception as e:
|
|
@@ -189,6 +173,66 @@ class ProjectInitializer:
|
|
|
189
173
|
print(f"✗ Failed to create .claude-mpm/ directory: {e}")
|
|
190
174
|
return False
|
|
191
175
|
|
|
176
|
+
def _verify_and_deploy_pm_skills(
|
|
177
|
+
self, project_root: Path, is_mcp_mode: bool = False
|
|
178
|
+
) -> None:
|
|
179
|
+
"""Verify PM skills are deployed and auto-deploy if missing.
|
|
180
|
+
|
|
181
|
+
Non-blocking operation that gracefully handles errors.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
project_root: Project root directory
|
|
185
|
+
is_mcp_mode: Whether running in MCP mode (suppress console output)
|
|
186
|
+
"""
|
|
187
|
+
try:
|
|
188
|
+
from claude_mpm.services.pm_skills_deployer import PMSkillsDeployerService
|
|
189
|
+
|
|
190
|
+
deployer = PMSkillsDeployerService()
|
|
191
|
+
result = deployer.verify_pm_skills(project_root)
|
|
192
|
+
|
|
193
|
+
if not result.verified:
|
|
194
|
+
# Log warnings
|
|
195
|
+
for warning in result.warnings:
|
|
196
|
+
self.logger.warning(warning)
|
|
197
|
+
|
|
198
|
+
# Auto-deploy PM skills
|
|
199
|
+
self.logger.info("Auto-deploying PM skills...")
|
|
200
|
+
deploy_result = deployer.deploy_pm_skills(project_root)
|
|
201
|
+
|
|
202
|
+
if deploy_result.success:
|
|
203
|
+
self.logger.info(
|
|
204
|
+
f"PM skills deployed: {len(deploy_result.deployed)} deployed, "
|
|
205
|
+
f"{len(deploy_result.skipped)} skipped"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Print to console if not in MCP mode
|
|
209
|
+
if not is_mcp_mode:
|
|
210
|
+
if deploy_result.deployed:
|
|
211
|
+
print(
|
|
212
|
+
f"✓ Deployed {len(deploy_result.deployed)} PM skill(s) "
|
|
213
|
+
f"to .claude-mpm/skills/pm/"
|
|
214
|
+
)
|
|
215
|
+
else:
|
|
216
|
+
self.logger.warning(
|
|
217
|
+
f"PM skills deployment had errors: {len(deploy_result.errors)}"
|
|
218
|
+
)
|
|
219
|
+
if not is_mcp_mode and deploy_result.errors:
|
|
220
|
+
print(f"⚠ PM skills deployment had {len(deploy_result.errors)} error(s)")
|
|
221
|
+
else:
|
|
222
|
+
# Skills verified successfully
|
|
223
|
+
registry = deployer._load_registry(project_root)
|
|
224
|
+
skill_count = len(registry.get("skills", []))
|
|
225
|
+
self.logger.debug(f"PM skills verified: {skill_count} skills")
|
|
226
|
+
|
|
227
|
+
if not is_mcp_mode and skill_count > 0:
|
|
228
|
+
print(f"✓ Verified {skill_count} PM skill(s)")
|
|
229
|
+
|
|
230
|
+
except ImportError:
|
|
231
|
+
self.logger.debug("PM skills deployer not available")
|
|
232
|
+
except Exception as e:
|
|
233
|
+
self.logger.warning(f"PM skills verification failed: {e}")
|
|
234
|
+
# Don't print to console - this is a non-critical failure
|
|
235
|
+
|
|
192
236
|
def _migrate_project_agents(self):
|
|
193
237
|
"""Migrate agents from old subdirectory structure to direct agents directory.
|
|
194
238
|
|
|
@@ -34,7 +34,7 @@ class GitRepository:
|
|
|
34
34
|
def cache_path(self) -> Path:
|
|
35
35
|
"""Return cache directory path for this repository.
|
|
36
36
|
|
|
37
|
-
Cache structure: ~/.claude-mpm/cache/
|
|
37
|
+
Cache structure: ~/.claude-mpm/cache/agents/{owner}/{repo}/{subdirectory}/
|
|
38
38
|
|
|
39
39
|
Returns:
|
|
40
40
|
Absolute path to cache directory for this repository
|
|
@@ -45,10 +45,10 @@ class GitRepository:
|
|
|
45
45
|
... subdirectory="agents"
|
|
46
46
|
... )
|
|
47
47
|
>>> repo.cache_path
|
|
48
|
-
Path('/Users/user/.claude-mpm/cache/
|
|
48
|
+
Path('/Users/user/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents/agents')
|
|
49
49
|
"""
|
|
50
50
|
home = Path.home()
|
|
51
|
-
base_cache = home / ".claude-mpm" / "cache" / "
|
|
51
|
+
base_cache = home / ".claude-mpm" / "cache" / "agents"
|
|
52
52
|
|
|
53
53
|
# Extract owner and repo from URL
|
|
54
54
|
owner, repo = self._parse_github_url(self.url)
|