claude-mpm 4.4.3__py3-none-any.whl → 4.4.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/agent_loader.py +3 -2
- claude_mpm/agents/agent_loader_integration.py +2 -1
- claude_mpm/agents/async_agent_loader.py +2 -2
- claude_mpm/agents/base_agent_loader.py +2 -2
- claude_mpm/agents/frontmatter_validator.py +1 -0
- claude_mpm/agents/system_agent_config.py +2 -1
- claude_mpm/cli/commands/doctor.py +44 -5
- claude_mpm/cli/commands/mpm_init.py +116 -62
- claude_mpm/cli/parsers/configure_parser.py +3 -1
- claude_mpm/cli/startup_logging.py +1 -3
- claude_mpm/config/agent_config.py +1 -1
- claude_mpm/config/paths.py +2 -1
- claude_mpm/core/agent_name_normalizer.py +1 -0
- claude_mpm/core/config.py +2 -1
- claude_mpm/core/config_aliases.py +2 -1
- claude_mpm/core/file_utils.py +0 -1
- claude_mpm/core/framework/__init__.py +6 -6
- claude_mpm/core/framework/formatters/__init__.py +2 -2
- claude_mpm/core/framework/formatters/capability_generator.py +19 -8
- claude_mpm/core/framework/formatters/content_formatter.py +8 -3
- claude_mpm/core/framework/formatters/context_generator.py +7 -3
- claude_mpm/core/framework/loaders/__init__.py +3 -3
- claude_mpm/core/framework/loaders/agent_loader.py +7 -3
- claude_mpm/core/framework/loaders/file_loader.py +16 -6
- claude_mpm/core/framework/loaders/instruction_loader.py +16 -6
- claude_mpm/core/framework/loaders/packaged_loader.py +36 -12
- claude_mpm/core/framework/processors/__init__.py +2 -2
- claude_mpm/core/framework/processors/memory_processor.py +14 -6
- claude_mpm/core/framework/processors/metadata_processor.py +5 -5
- claude_mpm/core/framework/processors/template_processor.py +12 -6
- claude_mpm/core/framework_loader.py +44 -20
- claude_mpm/core/log_manager.py +2 -1
- claude_mpm/core/tool_access_control.py +1 -0
- claude_mpm/core/unified_agent_registry.py +2 -1
- claude_mpm/core/unified_paths.py +1 -0
- claude_mpm/experimental/cli_enhancements.py +1 -0
- claude_mpm/hooks/base_hook.py +1 -0
- claude_mpm/hooks/instruction_reinforcement.py +1 -0
- claude_mpm/hooks/kuzu_memory_hook.py +20 -13
- claude_mpm/hooks/validation_hooks.py +1 -1
- claude_mpm/scripts/mpm_doctor.py +1 -0
- claude_mpm/services/agents/loading/agent_profile_loader.py +1 -1
- claude_mpm/services/agents/loading/base_agent_manager.py +1 -1
- claude_mpm/services/agents/loading/framework_agent_loader.py +1 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -0
- claude_mpm/services/agents/management/agent_management_service.py +1 -1
- claude_mpm/services/agents/memory/memory_categorization_service.py +0 -1
- claude_mpm/services/agents/memory/memory_file_service.py +6 -2
- claude_mpm/services/agents/memory/memory_format_service.py +0 -1
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
- claude_mpm/services/async_session_logger.py +1 -1
- claude_mpm/services/claude_session_logger.py +1 -0
- claude_mpm/services/core/path_resolver.py +1 -0
- claude_mpm/services/diagnostics/checks/__init__.py +2 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +126 -25
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +399 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +3 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +259 -32
- claude_mpm/services/event_bus/direct_relay.py +2 -1
- claude_mpm/services/event_bus/event_bus.py +1 -0
- claude_mpm/services/event_bus/relay.py +3 -2
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +1 -1
- claude_mpm/services/infrastructure/daemon_manager.py +1 -1
- claude_mpm/services/mcp_config_manager.py +10 -10
- claude_mpm/services/mcp_gateway/core/process_pool.py +62 -23
- claude_mpm/services/mcp_gateway/tools/__init__.py +6 -5
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +3 -1
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +16 -31
- claude_mpm/services/memory/cache/simple_cache.py +1 -1
- claude_mpm/services/project/archive_manager.py +159 -96
- claude_mpm/services/project/documentation_manager.py +64 -45
- claude_mpm/services/project/enhanced_analyzer.py +132 -89
- claude_mpm/services/project/project_organizer.py +225 -131
- claude_mpm/services/response_tracker.py +1 -1
- claude_mpm/services/socketio/server/eventbus_integration.py +1 -1
- claude_mpm/services/unified/__init__.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/__init__.py +3 -3
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +97 -53
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +81 -40
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +277 -178
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +196 -112
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +83 -49
- claude_mpm/services/unified/config_strategies/__init__.py +111 -126
- claude_mpm/services/unified/config_strategies/config_schema.py +157 -111
- claude_mpm/services/unified/config_strategies/context_strategy.py +91 -89
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +183 -173
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +160 -152
- claude_mpm/services/unified/config_strategies/unified_config_service.py +124 -112
- claude_mpm/services/unified/config_strategies/validation_strategy.py +298 -259
- claude_mpm/services/unified/deployment_strategies/__init__.py +7 -7
- claude_mpm/services/unified/deployment_strategies/base.py +24 -28
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +168 -88
- claude_mpm/services/unified/deployment_strategies/local.py +49 -34
- claude_mpm/services/unified/deployment_strategies/utils.py +39 -43
- claude_mpm/services/unified/deployment_strategies/vercel.py +30 -24
- claude_mpm/services/unified/interfaces.py +0 -26
- claude_mpm/services/unified/migration.py +17 -40
- claude_mpm/services/unified/strategies.py +9 -26
- claude_mpm/services/unified/unified_analyzer.py +48 -44
- claude_mpm/services/unified/unified_config.py +21 -19
- claude_mpm/services/unified/unified_deployment.py +21 -26
- claude_mpm/storage/state_storage.py +1 -0
- claude_mpm/utils/agent_dependency_loader.py +18 -6
- claude_mpm/utils/common.py +14 -12
- claude_mpm/utils/database_connector.py +15 -12
- claude_mpm/utils/error_handler.py +1 -0
- claude_mpm/utils/log_cleanup.py +1 -0
- claude_mpm/utils/path_operations.py +1 -0
- claude_mpm/utils/session_logging.py +1 -1
- claude_mpm/utils/subprocess_utils.py +1 -0
- claude_mpm/validation/agent_validator.py +1 -1
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/METADATA +9 -3
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/RECORD +118 -117
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/WHEEL +0 -0
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/top_level.txt +0 -0
@@ -242,40 +242,74 @@ class DoctorReporter:
|
|
242
242
|
print(json.dumps(output, indent=2))
|
243
243
|
|
244
244
|
def _report_markdown(self, summary: DiagnosticSummary):
|
245
|
-
"""Generate Markdown-formatted report."""
|
246
|
-
|
247
|
-
|
248
|
-
#
|
249
|
-
print("
|
250
|
-
print(
|
251
|
-
|
252
|
-
|
253
|
-
print(f"
|
254
|
-
print(
|
255
|
-
|
245
|
+
"""Generate comprehensive Markdown-formatted report."""
|
246
|
+
import datetime
|
247
|
+
|
248
|
+
# Header with timestamp
|
249
|
+
print("# Claude MPM Doctor Report")
|
250
|
+
print(
|
251
|
+
f"\n**Generated:** {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
252
|
+
)
|
253
|
+
print(f"**Version:** {self._get_version()}\n")
|
254
|
+
print("---\n")
|
255
|
+
|
256
|
+
# System Overview
|
257
|
+
print("## System Overview\n")
|
258
|
+
self._print_system_overview_markdown(summary)
|
256
259
|
print()
|
257
260
|
|
258
|
-
#
|
259
|
-
print("##
|
261
|
+
# Summary Statistics
|
262
|
+
print("## Summary Statistics\n")
|
263
|
+
print("| Status | Count | Percentage |")
|
264
|
+
print("|--------|-------|------------|")
|
265
|
+
total = (
|
266
|
+
summary.ok_count
|
267
|
+
+ summary.warning_count
|
268
|
+
+ summary.error_count
|
269
|
+
+ summary.skipped_count
|
270
|
+
)
|
271
|
+
if total > 0:
|
272
|
+
print(f"| ✅ OK | {summary.ok_count} | {summary.ok_count*100//total}% |")
|
273
|
+
print(
|
274
|
+
f"| ⚠️ Warning | {summary.warning_count} | {summary.warning_count*100//total}% |"
|
275
|
+
)
|
276
|
+
print(
|
277
|
+
f"| ❌ Error | {summary.error_count} | {summary.error_count*100//total}% |"
|
278
|
+
)
|
279
|
+
print(
|
280
|
+
f"| ⏭️ Skipped | {summary.skipped_count} | {summary.skipped_count*100//total}% |"
|
281
|
+
)
|
282
|
+
print()
|
283
|
+
|
284
|
+
# Overall Health Status
|
285
|
+
overall = summary.overall_status
|
286
|
+
if overall == DiagnosticStatus.OK:
|
287
|
+
print("### 🎉 Overall Status: **Healthy**")
|
288
|
+
print("Your Claude MPM installation is functioning properly.\n")
|
289
|
+
elif overall == DiagnosticStatus.WARNING:
|
290
|
+
print("### ⚠️ Overall Status: **Needs Attention**")
|
291
|
+
print("Your installation has minor issues that should be addressed.\n")
|
292
|
+
else:
|
293
|
+
print("### ❌ Overall Status: **Critical Issues**")
|
294
|
+
print(
|
295
|
+
"Your installation has critical issues that need immediate attention.\n"
|
296
|
+
)
|
297
|
+
|
298
|
+
# MCP Services Status (if available)
|
299
|
+
self._print_mcp_services_markdown(summary)
|
260
300
|
|
301
|
+
# Installation Details
|
302
|
+
self._print_installation_details_markdown(summary)
|
303
|
+
|
304
|
+
# Detailed Diagnostic Results
|
305
|
+
print("## Detailed Diagnostic Results\n")
|
261
306
|
for result in summary.results:
|
262
|
-
|
263
|
-
print(f"### {symbol} {result.category}\n")
|
264
|
-
print(f"**Status:** {result.status.value}")
|
265
|
-
print(f"**Message:** {result.message}\n")
|
266
|
-
|
267
|
-
if result.fix_command:
|
268
|
-
print(f"**Fix:** `{result.fix_command}`")
|
269
|
-
if result.fix_description:
|
270
|
-
print(f"_{result.fix_description}_\n")
|
271
|
-
|
272
|
-
if self.verbose and result.details:
|
273
|
-
print("**Details:**")
|
274
|
-
for key, value in result.details.items():
|
275
|
-
print(f"- {key}: {value}")
|
276
|
-
print()
|
307
|
+
self._print_result_markdown(result)
|
277
308
|
|
278
|
-
#
|
309
|
+
# Recommendations Section
|
310
|
+
self._print_recommendations_markdown(summary)
|
311
|
+
|
312
|
+
# Fixes Section
|
279
313
|
fixes = [
|
280
314
|
(r.category, r.fix_command, r.fix_description)
|
281
315
|
for r in summary.results
|
@@ -283,13 +317,24 @@ class DoctorReporter:
|
|
283
317
|
]
|
284
318
|
|
285
319
|
if fixes:
|
286
|
-
print("## Suggested Fixes\n")
|
320
|
+
print("## 🔧 Suggested Fixes\n")
|
321
|
+
print("Run these commands to fix identified issues:\n")
|
322
|
+
print("```bash")
|
287
323
|
for category, command, description in fixes:
|
288
|
-
print(f"
|
324
|
+
print(f"# Fix: {category}")
|
289
325
|
if description:
|
290
|
-
print(f"
|
326
|
+
print(f"# {description}")
|
327
|
+
print(command)
|
328
|
+
print()
|
329
|
+
print("```")
|
291
330
|
print()
|
292
331
|
|
332
|
+
# Footer
|
333
|
+
print("---")
|
334
|
+
print(
|
335
|
+
"\n*For more information, run `claude-mpm doctor --verbose` or visit the [documentation](https://github.com/bobmatnyc/claude-mpm).*"
|
336
|
+
)
|
337
|
+
|
293
338
|
def _color(self, text: str, color: str) -> str:
|
294
339
|
"""Apply color to text if colors are enabled."""
|
295
340
|
if not self.use_color:
|
@@ -318,3 +363,185 @@ class DoctorReporter:
|
|
318
363
|
return service.get_version()
|
319
364
|
except Exception:
|
320
365
|
return "unknown"
|
366
|
+
|
367
|
+
def _print_system_overview_markdown(self, summary: DiagnosticSummary):
|
368
|
+
"""Print system overview in markdown format."""
|
369
|
+
import platform
|
370
|
+
import sys
|
371
|
+
|
372
|
+
print("| Component | Value |")
|
373
|
+
print("|-----------|-------|")
|
374
|
+
print(f"| Platform | {platform.system()} {platform.release()} |")
|
375
|
+
print(f"| Python Version | {sys.version.split()[0]} |")
|
376
|
+
|
377
|
+
# Extract key details from results
|
378
|
+
for result in summary.results:
|
379
|
+
if result.category == "Installation":
|
380
|
+
if result.details.get("claude_mpm_version"):
|
381
|
+
print(
|
382
|
+
f"| Claude MPM Version | {result.details['claude_mpm_version']} |"
|
383
|
+
)
|
384
|
+
if result.details.get("installation_method"):
|
385
|
+
print(
|
386
|
+
f"| Installation Method | {result.details['installation_method']} |"
|
387
|
+
)
|
388
|
+
elif result.category == "Claude Desktop":
|
389
|
+
if result.details.get("version"):
|
390
|
+
print(f"| Claude Desktop | {result.details['version']} |")
|
391
|
+
|
392
|
+
def _print_mcp_services_markdown(self, summary: DiagnosticSummary):
|
393
|
+
"""Print MCP services status table in markdown."""
|
394
|
+
# Find MCP Services result
|
395
|
+
mcp_services_result = None
|
396
|
+
for result in summary.results:
|
397
|
+
if result.category == "MCP Services":
|
398
|
+
mcp_services_result = result
|
399
|
+
break
|
400
|
+
|
401
|
+
if not mcp_services_result or not mcp_services_result.details.get("services"):
|
402
|
+
return
|
403
|
+
|
404
|
+
print("## MCP Services Status\n")
|
405
|
+
print("| Service | Installed | Accessible | Version | Status |")
|
406
|
+
print("|---------|-----------|------------|---------|--------|")
|
407
|
+
|
408
|
+
services = mcp_services_result.details.get("services", {})
|
409
|
+
for service_name, service_info in services.items():
|
410
|
+
installed = "✅" if service_info.get("installed") else "❌"
|
411
|
+
accessible = "✅" if service_info.get("accessible") else "❌"
|
412
|
+
version = service_info.get("version")
|
413
|
+
if version:
|
414
|
+
version = str(version)[:20] # Truncate long versions
|
415
|
+
else:
|
416
|
+
version = "N/A"
|
417
|
+
status = service_info.get("status", "unknown")
|
418
|
+
|
419
|
+
# Map status to emoji
|
420
|
+
if status == "ok":
|
421
|
+
status_emoji = "✅"
|
422
|
+
elif status == "warning":
|
423
|
+
status_emoji = "⚠️"
|
424
|
+
elif status == "error":
|
425
|
+
status_emoji = "❌"
|
426
|
+
else:
|
427
|
+
status_emoji = "❓"
|
428
|
+
|
429
|
+
print(
|
430
|
+
f"| {service_name} | {installed} | {accessible} | {version} | {status_emoji} |"
|
431
|
+
)
|
432
|
+
print()
|
433
|
+
|
434
|
+
def _print_installation_details_markdown(self, summary: DiagnosticSummary):
|
435
|
+
"""Print installation details in markdown."""
|
436
|
+
# Find installation result
|
437
|
+
install_result = None
|
438
|
+
for result in summary.results:
|
439
|
+
if result.category == "Installation":
|
440
|
+
install_result = result
|
441
|
+
break
|
442
|
+
|
443
|
+
if not install_result:
|
444
|
+
return
|
445
|
+
|
446
|
+
print("## Installation Details\n")
|
447
|
+
|
448
|
+
# Print sub-results if available
|
449
|
+
if install_result.sub_results and self.verbose:
|
450
|
+
for sub in install_result.sub_results:
|
451
|
+
if sub.category == "Python Version":
|
452
|
+
print("### Python Environment")
|
453
|
+
print(f"- **Version:** {sub.message}")
|
454
|
+
elif sub.category == "Installation Method":
|
455
|
+
print("### Installation Method")
|
456
|
+
print(f"- **Method:** {sub.message}")
|
457
|
+
if sub.details.get("container_type"):
|
458
|
+
print(f"- **Container:** {sub.details['container_type']}")
|
459
|
+
if sub.details.get("pipx_metadata"):
|
460
|
+
metadata = sub.details["pipx_metadata"]
|
461
|
+
if metadata.get("version"):
|
462
|
+
print(f"- **Pipx Version:** {metadata['version']}")
|
463
|
+
elif sub.category == "Dependencies":
|
464
|
+
print("### Dependencies")
|
465
|
+
print(f"- **Status:** {sub.message}")
|
466
|
+
if sub.details.get("installed"):
|
467
|
+
print(
|
468
|
+
f"- **Installed Packages:** {len(sub.details['installed'])}"
|
469
|
+
)
|
470
|
+
print()
|
471
|
+
|
472
|
+
def _print_result_markdown(self, result: DiagnosticResult):
|
473
|
+
"""Print a single result in markdown format."""
|
474
|
+
symbol = self.STATUS_SYMBOLS.get(result.status, "?")
|
475
|
+
print(f"### {symbol} {result.category}\n")
|
476
|
+
|
477
|
+
# Status badge
|
478
|
+
status_badge = {
|
479
|
+
DiagnosticStatus.OK: "",
|
480
|
+
DiagnosticStatus.WARNING: "",
|
481
|
+
DiagnosticStatus.ERROR: "",
|
482
|
+
DiagnosticStatus.SKIPPED: "",
|
483
|
+
}.get(result.status, "")
|
484
|
+
|
485
|
+
print(f"{status_badge}")
|
486
|
+
print(f"\n**Message:** {result.message}\n")
|
487
|
+
|
488
|
+
if result.fix_command:
|
489
|
+
print("**Fix Available:**")
|
490
|
+
print("```bash")
|
491
|
+
print(result.fix_command)
|
492
|
+
print("```")
|
493
|
+
if result.fix_description:
|
494
|
+
print(f"*{result.fix_description}*\n")
|
495
|
+
|
496
|
+
if self.verbose and result.details:
|
497
|
+
print("<details>")
|
498
|
+
print("<summary>Details</summary>\n")
|
499
|
+
print("```json")
|
500
|
+
import json
|
501
|
+
|
502
|
+
print(json.dumps(result.details, indent=2, default=str))
|
503
|
+
print("```")
|
504
|
+
print("</details>\n")
|
505
|
+
|
506
|
+
def _print_recommendations_markdown(self, summary: DiagnosticSummary):
|
507
|
+
"""Print recommendations based on diagnostic results."""
|
508
|
+
recommendations = []
|
509
|
+
|
510
|
+
# Analyze results for recommendations
|
511
|
+
has_errors = summary.error_count > 0
|
512
|
+
has_warnings = summary.warning_count > 0
|
513
|
+
|
514
|
+
# Check specific conditions
|
515
|
+
for result in summary.results:
|
516
|
+
if (
|
517
|
+
result.category == "Installation"
|
518
|
+
and result.status != DiagnosticStatus.OK
|
519
|
+
):
|
520
|
+
if "pipx" not in str(result.details.get("installation_method", "")):
|
521
|
+
recommendations.append(
|
522
|
+
"Consider using pipx for isolated installation: `pipx install claude-mpm`"
|
523
|
+
)
|
524
|
+
|
525
|
+
if result.category == "MCP Services":
|
526
|
+
services = result.details.get("services", {})
|
527
|
+
missing = [
|
528
|
+
s for s, info in services.items() if not info.get("installed")
|
529
|
+
]
|
530
|
+
if missing:
|
531
|
+
recommendations.append(
|
532
|
+
f"Install missing MCP services for enhanced features: {', '.join(missing)}"
|
533
|
+
)
|
534
|
+
|
535
|
+
if (
|
536
|
+
result.category == "Claude Desktop"
|
537
|
+
and result.status == DiagnosticStatus.WARNING
|
538
|
+
):
|
539
|
+
recommendations.append(
|
540
|
+
"Update Claude Desktop to the latest version for best compatibility"
|
541
|
+
)
|
542
|
+
|
543
|
+
if recommendations:
|
544
|
+
print("## 📋 Recommendations\n")
|
545
|
+
for i, rec in enumerate(recommendations, 1):
|
546
|
+
print(f"{i}. {rec}")
|
547
|
+
print()
|
@@ -18,9 +18,10 @@ import logging
|
|
18
18
|
from datetime import datetime, timezone
|
19
19
|
from typing import Any
|
20
20
|
|
21
|
+
from claude_mpm.core.logging_utils import get_logger
|
22
|
+
|
21
23
|
from .event_bus import EventBus
|
22
24
|
|
23
|
-
from claude_mpm.core.logging_utils import get_logger
|
24
25
|
logger = get_logger(__name__)
|
25
26
|
|
26
27
|
|
@@ -9,10 +9,10 @@ from collections import OrderedDict
|
|
9
9
|
from datetime import datetime, timezone
|
10
10
|
from typing import Any, Dict, Optional
|
11
11
|
|
12
|
+
from claude_mpm.core.logging_utils import get_logger
|
12
13
|
from claude_mpm.services.agents.management import AgentCapabilitiesGenerator
|
13
14
|
from claude_mpm.services.agents.registry import DeployedAgentDiscovery
|
14
15
|
|
15
|
-
from claude_mpm.core.logging_utils import get_logger
|
16
16
|
logger = get_logger(__name__)
|
17
17
|
|
18
18
|
|
@@ -31,9 +31,9 @@ except ImportError:
|
|
31
31
|
import psutil
|
32
32
|
|
33
33
|
# from claude_mpm.core.base_service import BaseService
|
34
|
+
from claude_mpm.core.logging_utils import get_logger
|
34
35
|
from claude_mpm.services.socketio.server.main import SocketIOServer
|
35
36
|
|
36
|
-
from claude_mpm.core.logging_utils import get_logger
|
37
37
|
logger = get_logger(__name__)
|
38
38
|
|
39
39
|
|
@@ -76,6 +76,7 @@ class MCPConfigManager:
|
|
76
76
|
|
77
77
|
# Check system PATH (including homebrew)
|
78
78
|
import shutil
|
79
|
+
|
79
80
|
system_path = shutil.which(service_name)
|
80
81
|
if system_path and system_path not in candidates:
|
81
82
|
candidates.append(system_path)
|
@@ -84,14 +85,13 @@ class MCPConfigManager:
|
|
84
85
|
for path in candidates:
|
85
86
|
try:
|
86
87
|
result = subprocess.run(
|
87
|
-
[path, "--help"],
|
88
|
-
capture_output=True,
|
89
|
-
text=True,
|
90
|
-
timeout=5
|
88
|
+
[path, "--help"], capture_output=True, text=True, timeout=5, check=False
|
91
89
|
)
|
92
90
|
# Check if this version has MCP support
|
93
91
|
if "claude" in result.stdout or "mcp" in result.stdout:
|
94
|
-
self.logger.debug(
|
92
|
+
self.logger.debug(
|
93
|
+
f"Found kuzu-memory with MCP support at {path}"
|
94
|
+
)
|
95
95
|
return path
|
96
96
|
except:
|
97
97
|
pass
|
@@ -125,7 +125,9 @@ class MCPConfigManager:
|
|
125
125
|
)
|
126
126
|
return local_path
|
127
127
|
|
128
|
-
self.logger.debug(
|
128
|
+
self.logger.debug(
|
129
|
+
f"Service {service_name} not found - will auto-install when needed"
|
130
|
+
)
|
129
131
|
return None
|
130
132
|
|
131
133
|
def _check_pipx_installation(self, service_name: str) -> Optional[str]:
|
@@ -224,12 +226,10 @@ class MCPConfigManager:
|
|
224
226
|
# Check kuzu-memory version to determine correct command
|
225
227
|
# v1.1.0+ has "claude mcp-server", v1.0.0 has "serve"
|
226
228
|
import subprocess
|
229
|
+
|
227
230
|
try:
|
228
231
|
result = subprocess.run(
|
229
|
-
[service_path, "--help"],
|
230
|
-
capture_output=True,
|
231
|
-
text=True,
|
232
|
-
timeout=10
|
232
|
+
[service_path, "--help"], capture_output=True, text=True, timeout=10, check=False
|
233
233
|
)
|
234
234
|
if "claude" in result.stdout:
|
235
235
|
# v1.1.0+ with claude command
|
@@ -395,6 +395,7 @@ async def auto_initialize_vector_search():
|
|
395
395
|
try:
|
396
396
|
# Import MCPConfigManager to handle installation
|
397
397
|
from claude_mpm.services.mcp_config_manager import MCPConfigManager
|
398
|
+
|
398
399
|
config_manager = MCPConfigManager()
|
399
400
|
|
400
401
|
# Check if mcp-vector-search is already installed
|
@@ -411,7 +412,9 @@ async def auto_initialize_vector_search():
|
|
411
412
|
import subprocess
|
412
413
|
|
413
414
|
if not shutil.which("pipx"):
|
414
|
-
logger.warning(
|
415
|
+
logger.warning(
|
416
|
+
"⚠️ pipx not found. Please install pipx to enable automatic mcp-vector-search installation"
|
417
|
+
)
|
415
418
|
logger.info(" Install pipx with: python -m pip install --user pipx")
|
416
419
|
return
|
417
420
|
|
@@ -420,26 +423,34 @@ async def auto_initialize_vector_search():
|
|
420
423
|
["pipx", "install", "mcp-vector-search"],
|
421
424
|
capture_output=True,
|
422
425
|
text=True,
|
423
|
-
timeout=60 # 1 minute timeout for installation
|
426
|
+
timeout=60, check=False, # 1 minute timeout for installation
|
424
427
|
)
|
425
428
|
|
426
429
|
if result.returncode == 0:
|
427
430
|
logger.info("✅ mcp-vector-search installed successfully")
|
428
431
|
# Detect the newly installed path
|
429
|
-
vector_search_path = config_manager.detect_service_path(
|
432
|
+
vector_search_path = config_manager.detect_service_path(
|
433
|
+
"mcp-vector-search"
|
434
|
+
)
|
430
435
|
if not vector_search_path:
|
431
|
-
logger.warning(
|
436
|
+
logger.warning(
|
437
|
+
"mcp-vector-search installed but command not found in PATH"
|
438
|
+
)
|
432
439
|
return
|
433
440
|
|
434
441
|
# Update the Claude configuration to include the newly installed service
|
435
442
|
logger.info("📝 Updating Claude configuration...")
|
436
|
-
config_success, config_msg =
|
443
|
+
config_success, config_msg = (
|
444
|
+
config_manager.ensure_mcp_services_configured()
|
445
|
+
)
|
437
446
|
if config_success:
|
438
447
|
logger.info(f"✅ {config_msg}")
|
439
448
|
else:
|
440
449
|
logger.warning(f"⚠️ Configuration update issue: {config_msg}")
|
441
450
|
else:
|
442
|
-
logger.warning(
|
451
|
+
logger.warning(
|
452
|
+
f"Failed to install mcp-vector-search: {result.stderr}"
|
453
|
+
)
|
443
454
|
return
|
444
455
|
|
445
456
|
except subprocess.TimeoutExpired:
|
@@ -452,10 +463,14 @@ async def auto_initialize_vector_search():
|
|
452
463
|
# At this point, mcp-vector-search should be available
|
453
464
|
# Get the actual command to use
|
454
465
|
import shutil
|
466
|
+
|
455
467
|
vector_search_cmd = shutil.which("mcp-vector-search")
|
456
468
|
if not vector_search_cmd:
|
457
469
|
# Try pipx installation path as fallback
|
458
|
-
pipx_path =
|
470
|
+
pipx_path = (
|
471
|
+
Path.home()
|
472
|
+
/ ".local/pipx/venvs/mcp-vector-search/bin/mcp-vector-search"
|
473
|
+
)
|
459
474
|
if pipx_path.exists():
|
460
475
|
vector_search_cmd = str(pipx_path)
|
461
476
|
else:
|
@@ -477,10 +492,13 @@ async def auto_initialize_vector_search():
|
|
477
492
|
if chroma_db.exists() and chroma_db.stat().st_size > 0:
|
478
493
|
logger.info("✓ Vector search index is healthy and ready")
|
479
494
|
return
|
480
|
-
|
481
|
-
|
495
|
+
logger.info(
|
496
|
+
"⚠️ Vector search index may be corrupted, rebuilding..."
|
497
|
+
)
|
482
498
|
except Exception as e:
|
483
|
-
logger.debug(
|
499
|
+
logger.debug(
|
500
|
+
f"Vector search health check failed: {e}, will attempt to rebuild"
|
501
|
+
)
|
484
502
|
|
485
503
|
# Initialize or reinitialize the project
|
486
504
|
logger.info(f"🎯 Initializing vector search for project: {current_dir}")
|
@@ -488,12 +506,13 @@ async def auto_initialize_vector_search():
|
|
488
506
|
# Initialize the project (this creates the config)
|
489
507
|
# Note: mcp-vector-search operates on the current directory
|
490
508
|
import subprocess
|
509
|
+
|
491
510
|
proc = subprocess.run(
|
492
511
|
[vector_search_cmd, "init"],
|
493
512
|
capture_output=True,
|
494
513
|
text=True,
|
495
514
|
timeout=30,
|
496
|
-
cwd=str(current_dir) # Run in the project directory
|
515
|
+
cwd=str(current_dir), check=False, # Run in the project directory
|
497
516
|
)
|
498
517
|
|
499
518
|
if proc.returncode == 0:
|
@@ -508,29 +527,36 @@ async def auto_initialize_vector_search():
|
|
508
527
|
capture_output=True,
|
509
528
|
text=True,
|
510
529
|
timeout=300, # 5 minute timeout for indexing
|
511
|
-
cwd=str(current_dir) # Run in the project directory
|
530
|
+
cwd=str(current_dir), check=False, # Run in the project directory
|
512
531
|
)
|
513
532
|
if index_proc.returncode == 0:
|
514
533
|
logger.info("✅ Project indexing completed successfully")
|
515
534
|
# Parse output to show statistics if available
|
516
535
|
if "indexed" in index_proc.stdout.lower():
|
517
536
|
# Extract and log indexing statistics
|
518
|
-
lines = index_proc.stdout.strip().split(
|
537
|
+
lines = index_proc.stdout.strip().split("\n")
|
519
538
|
for line in lines:
|
520
539
|
if "indexed" in line.lower() or "files" in line.lower():
|
521
540
|
logger.info(f" {line.strip()}")
|
522
541
|
else:
|
523
|
-
logger.warning(
|
542
|
+
logger.warning(
|
543
|
+
f"⚠️ Project indexing failed: {index_proc.stderr}"
|
544
|
+
)
|
524
545
|
except subprocess.TimeoutExpired:
|
525
|
-
logger.warning(
|
546
|
+
logger.warning(
|
547
|
+
"⚠️ Project indexing timed out (will continue in background)"
|
548
|
+
)
|
526
549
|
except Exception as e:
|
527
550
|
logger.debug(f"Background indexing error (non-critical): {e}")
|
528
551
|
|
529
552
|
# Run indexing in background thread
|
530
553
|
import threading
|
554
|
+
|
531
555
|
index_thread = threading.Thread(target=background_index, daemon=True)
|
532
556
|
index_thread.start()
|
533
|
-
logger.info(
|
557
|
+
logger.info(
|
558
|
+
"📚 Background indexing started - vector search will be available shortly"
|
559
|
+
)
|
534
560
|
|
535
561
|
else:
|
536
562
|
logger.warning(f"⚠️ Vector search initialization failed: {proc.stderr}")
|
@@ -556,6 +582,7 @@ async def auto_initialize_kuzu_memory():
|
|
556
582
|
try:
|
557
583
|
# Import MCPConfigManager to handle installation
|
558
584
|
from claude_mpm.services.mcp_config_manager import MCPConfigManager
|
585
|
+
|
559
586
|
config_manager = MCPConfigManager()
|
560
587
|
|
561
588
|
# Check if kuzu-memory is already installed
|
@@ -572,7 +599,9 @@ async def auto_initialize_kuzu_memory():
|
|
572
599
|
import subprocess
|
573
600
|
|
574
601
|
if not shutil.which("pipx"):
|
575
|
-
logger.warning(
|
602
|
+
logger.warning(
|
603
|
+
"⚠️ pipx not found. Please install pipx to enable automatic kuzu-memory installation"
|
604
|
+
)
|
576
605
|
logger.info(" Install pipx with: python -m pip install --user pipx")
|
577
606
|
return
|
578
607
|
|
@@ -581,7 +610,7 @@ async def auto_initialize_kuzu_memory():
|
|
581
610
|
["pipx", "install", "kuzu-memory"],
|
582
611
|
capture_output=True,
|
583
612
|
text=True,
|
584
|
-
timeout=60 # 1 minute timeout for installation
|
613
|
+
timeout=60, check=False, # 1 minute timeout for installation
|
585
614
|
)
|
586
615
|
|
587
616
|
if result.returncode == 0:
|
@@ -589,12 +618,16 @@ async def auto_initialize_kuzu_memory():
|
|
589
618
|
# Detect the newly installed path
|
590
619
|
kuzu_memory_path = config_manager.detect_service_path("kuzu-memory")
|
591
620
|
if not kuzu_memory_path:
|
592
|
-
logger.warning(
|
621
|
+
logger.warning(
|
622
|
+
"kuzu-memory installed but command not found in PATH"
|
623
|
+
)
|
593
624
|
return
|
594
625
|
|
595
626
|
# Update the Claude configuration to include the newly installed service
|
596
627
|
logger.info("📝 Updating Claude configuration...")
|
597
|
-
config_success, config_msg =
|
628
|
+
config_success, config_msg = (
|
629
|
+
config_manager.ensure_mcp_services_configured()
|
630
|
+
)
|
598
631
|
if config_success:
|
599
632
|
logger.info(f"✅ {config_msg}")
|
600
633
|
else:
|
@@ -613,6 +646,7 @@ async def auto_initialize_kuzu_memory():
|
|
613
646
|
# At this point, kuzu-memory should be available
|
614
647
|
# Get the actual command to use
|
615
648
|
import shutil
|
649
|
+
|
616
650
|
kuzu_memory_cmd = shutil.which("kuzu-memory")
|
617
651
|
if not kuzu_memory_cmd:
|
618
652
|
# Try pipx installation path as fallback
|
@@ -629,18 +663,23 @@ async def auto_initialize_kuzu_memory():
|
|
629
663
|
|
630
664
|
# Check if database is already initialized
|
631
665
|
if kuzu_memories_dir.exists():
|
632
|
-
logger.debug(
|
666
|
+
logger.debug(
|
667
|
+
f"Kuzu-memory database already initialized at {kuzu_memories_dir}"
|
668
|
+
)
|
633
669
|
else:
|
634
|
-
logger.info(
|
670
|
+
logger.info(
|
671
|
+
f"🎯 Initializing kuzu-memory database for project: {current_dir}"
|
672
|
+
)
|
635
673
|
|
636
674
|
# Initialize the database in current project directory
|
637
675
|
import subprocess
|
676
|
+
|
638
677
|
proc = subprocess.run(
|
639
678
|
[kuzu_memory_cmd, "init"],
|
640
679
|
capture_output=True,
|
641
680
|
text=True,
|
642
681
|
timeout=30,
|
643
|
-
cwd=str(current_dir),
|
682
|
+
cwd=str(current_dir), check=False,
|
644
683
|
)
|
645
684
|
|
646
685
|
if proc.returncode == 0:
|