This is page 43 of 62. Use http://codebase.md/doobidoo/mcp-memory-service?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .claude
│ ├── agents
│ │ ├── amp-bridge.md
│ │ ├── amp-pr-automator.md
│ │ ├── code-quality-guard.md
│ │ ├── gemini-pr-automator.md
│ │ └── github-release-manager.md
│ ├── commands
│ │ ├── README.md
│ │ ├── refactor-function
│ │ ├── refactor-function-prod
│ │ └── refactor-function.md
│ ├── consolidation-fix-handoff.md
│ ├── consolidation-hang-fix-summary.md
│ ├── directives
│ │ ├── agents.md
│ │ ├── code-quality-workflow.md
│ │ ├── consolidation-details.md
│ │ ├── development-setup.md
│ │ ├── hooks-configuration.md
│ │ ├── memory-first.md
│ │ ├── memory-tagging.md
│ │ ├── pr-workflow.md
│ │ ├── quality-system-details.md
│ │ ├── README.md
│ │ ├── refactoring-checklist.md
│ │ ├── storage-backends.md
│ │ └── version-management.md
│ ├── prompts
│ │ └── hybrid-cleanup-integration.md
│ ├── settings.local.json.backup
│ └── settings.local.json.local
├── .commit-message
├── .coveragerc
├── .dockerignore
├── .env.example
├── .env.sqlite.backup
├── .envnn#
├── .gitattributes
├── .github
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ ├── feature_request.yml
│ │ └── performance_issue.yml
│ ├── pull_request_template.md
│ └── workflows
│ ├── bridge-tests.yml
│ ├── CACHE_FIX.md
│ ├── claude-branch-automation.yml
│ ├── claude-code-review.yml
│ ├── claude.yml
│ ├── cleanup-images.yml.disabled
│ ├── dev-setup-validation.yml
│ ├── docker-publish.yml
│ ├── dockerfile-lint.yml
│ ├── LATEST_FIXES.md
│ ├── main-optimized.yml.disabled
│ ├── main.yml
│ ├── publish-and-test.yml
│ ├── publish-dual.yml
│ ├── README_OPTIMIZATION.md
│ ├── release-tag.yml.disabled
│ ├── release.yml
│ ├── roadmap-review-reminder.yml
│ ├── SECRET_CONDITIONAL_FIX.md
│ └── WORKFLOW_FIXES.md
├── .gitignore
├── .mcp.json.backup
├── .mcp.json.template
├── .metrics
│ ├── baseline_cc_install_hooks.txt
│ ├── baseline_mi_install_hooks.txt
│ ├── baseline_nesting_install_hooks.txt
│ ├── BASELINE_REPORT.md
│ ├── COMPLEXITY_COMPARISON.txt
│ ├── QUICK_REFERENCE.txt
│ ├── README.md
│ ├── REFACTORED_BASELINE.md
│ ├── REFACTORING_COMPLETION_REPORT.md
│ └── TRACKING_TABLE.md
├── .pyscn
│ ├── .gitignore
│ └── reports
│ └── analyze_20251123_214224.html
├── AGENTS.md
├── ai-optimized-tool-descriptions.py
├── archive
│ ├── deployment
│ │ ├── deploy_fastmcp_fixed.sh
│ │ ├── deploy_http_with_mcp.sh
│ │ └── deploy_mcp_v4.sh
│ ├── deployment-configs
│ │ ├── empty_config.yml
│ │ └── smithery.yaml
│ ├── development
│ │ └── test_fastmcp.py
│ ├── docs-removed-2025-08-23
│ │ ├── authentication.md
│ │ ├── claude_integration.md
│ │ ├── claude-code-compatibility.md
│ │ ├── claude-code-integration.md
│ │ ├── claude-code-quickstart.md
│ │ ├── claude-desktop-setup.md
│ │ ├── complete-setup-guide.md
│ │ ├── database-synchronization.md
│ │ ├── development
│ │ │ ├── autonomous-memory-consolidation.md
│ │ │ ├── CLEANUP_PLAN.md
│ │ │ ├── CLEANUP_README.md
│ │ │ ├── CLEANUP_SUMMARY.md
│ │ │ ├── dream-inspired-memory-consolidation.md
│ │ │ ├── hybrid-slm-memory-consolidation.md
│ │ │ ├── mcp-milestone.md
│ │ │ ├── multi-client-architecture.md
│ │ │ ├── test-results.md
│ │ │ └── TIMESTAMP_FIX_SUMMARY.md
│ │ ├── distributed-sync.md
│ │ ├── invocation_guide.md
│ │ ├── macos-intel.md
│ │ ├── master-guide.md
│ │ ├── mcp-client-configuration.md
│ │ ├── multi-client-server.md
│ │ ├── service-installation.md
│ │ ├── sessions
│ │ │ └── MCP_ENHANCEMENT_SESSION_MEMORY_v4.1.0.md
│ │ ├── UBUNTU_SETUP.md
│ │ ├── ubuntu.md
│ │ ├── windows-setup.md
│ │ └── windows.md
│ ├── docs-root-cleanup-2025-08-23
│ │ ├── AWESOME_LIST_SUBMISSION.md
│ │ ├── CLOUDFLARE_IMPLEMENTATION.md
│ │ ├── DOCUMENTATION_ANALYSIS.md
│ │ ├── DOCUMENTATION_CLEANUP_PLAN.md
│ │ ├── DOCUMENTATION_CONSOLIDATION_COMPLETE.md
│ │ ├── LITESTREAM_SETUP_GUIDE.md
│ │ ├── lm_studio_system_prompt.md
│ │ ├── PYTORCH_DOWNLOAD_FIX.md
│ │ └── README-ORIGINAL-BACKUP.md
│ ├── investigations
│ │ └── MACOS_HOOKS_INVESTIGATION.md
│ ├── litestream-configs-v6.3.0
│ │ ├── install_service.sh
│ │ ├── litestream_master_config_fixed.yml
│ │ ├── litestream_master_config.yml
│ │ ├── litestream_replica_config_fixed.yml
│ │ ├── litestream_replica_config.yml
│ │ ├── litestream_replica_simple.yml
│ │ ├── litestream-http.service
│ │ ├── litestream.service
│ │ └── requirements-cloudflare.txt
│ ├── release-notes
│ │ └── release-notes-v7.1.4.md
│ └── setup-development
│ ├── README.md
│ ├── setup_consolidation_mdns.sh
│ ├── STARTUP_SETUP_GUIDE.md
│ └── test_service.sh
├── CHANGELOG-HISTORIC.md
├── CHANGELOG.md
├── claude_commands
│ ├── memory-context.md
│ ├── memory-health.md
│ ├── memory-ingest-dir.md
│ ├── memory-ingest.md
│ ├── memory-recall.md
│ ├── memory-search.md
│ ├── memory-store.md
│ ├── README.md
│ └── session-start.md
├── claude-hooks
│ ├── config.json
│ ├── config.template.json
│ ├── CONFIGURATION.md
│ ├── core
│ │ ├── auto-capture-hook.js
│ │ ├── auto-capture-hook.ps1
│ │ ├── memory-retrieval.js
│ │ ├── mid-conversation.js
│ │ ├── permission-request.js
│ │ ├── session-end.js
│ │ ├── session-start.js
│ │ └── topic-change.js
│ ├── debug-pattern-test.js
│ ├── install_claude_hooks_windows.ps1
│ ├── install_hooks.py
│ ├── memory-mode-controller.js
│ ├── MIGRATION.md
│ ├── README-AUTO-CAPTURE.md
│ ├── README-NATURAL-TRIGGERS.md
│ ├── README-PERMISSION-REQUEST.md
│ ├── README-phase2.md
│ ├── README.md
│ ├── simple-test.js
│ ├── statusline.sh
│ ├── test-adaptive-weights.js
│ ├── test-dual-protocol-hook.js
│ ├── test-mcp-hook.js
│ ├── test-natural-triggers.js
│ ├── test-recency-scoring.js
│ ├── tests
│ │ ├── integration-test.js
│ │ ├── phase2-integration-test.js
│ │ ├── test-code-execution.js
│ │ ├── test-cross-session.json
│ │ ├── test-permission-request.js
│ │ ├── test-session-tracking.json
│ │ └── test-threading.json
│ ├── utilities
│ │ ├── adaptive-pattern-detector.js
│ │ ├── auto-capture-patterns.js
│ │ ├── context-formatter.js
│ │ ├── context-shift-detector.js
│ │ ├── conversation-analyzer.js
│ │ ├── dynamic-context-updater.js
│ │ ├── git-analyzer.js
│ │ ├── mcp-client.js
│ │ ├── memory-client.js
│ │ ├── memory-scorer.js
│ │ ├── performance-manager.js
│ │ ├── project-detector.js
│ │ ├── session-cache.json
│ │ ├── session-tracker.js
│ │ ├── tiered-conversation-monitor.js
│ │ ├── user-override-detector.js
│ │ └── version-checker.js
│ └── WINDOWS-SESSIONSTART-BUG.md
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── COMMIT_MESSAGE.md
├── CONTRIBUTING.md
├── Development-Sprint-November-2025.md
├── docs
│ ├── amp-cli-bridge.md
│ ├── api
│ │ ├── code-execution-interface.md
│ │ ├── memory-metadata-api.md
│ │ ├── PHASE1_IMPLEMENTATION_SUMMARY.md
│ │ ├── PHASE2_IMPLEMENTATION_SUMMARY.md
│ │ ├── PHASE2_REPORT.md
│ │ └── tag-standardization.md
│ ├── architecture
│ │ ├── graph-database-design.md
│ │ ├── search-enhancement-spec.md
│ │ └── search-examples.md
│ ├── architecture.md
│ ├── archive
│ │ └── obsolete-workflows
│ │ ├── load_memory_context.md
│ │ └── README.md
│ ├── assets
│ │ └── images
│ │ ├── dashboard-v3.3.0-preview.png
│ │ ├── memory-awareness-hooks-example.png
│ │ ├── project-infographic.svg
│ │ └── README.md
│ ├── CLAUDE_CODE_QUICK_REFERENCE.md
│ ├── cloudflare-setup.md
│ ├── demo-recording-script.md
│ ├── deployment
│ │ ├── docker.md
│ │ ├── dual-service.md
│ │ ├── production-guide.md
│ │ └── systemd-service.md
│ ├── development
│ │ ├── ai-agent-instructions.md
│ │ ├── code-quality
│ │ │ ├── phase-2a-completion.md
│ │ │ ├── phase-2a-handle-get-prompt.md
│ │ │ ├── phase-2a-index.md
│ │ │ ├── phase-2a-install-package.md
│ │ │ └── phase-2b-session-summary.md
│ │ ├── code-quality-workflow.md
│ │ ├── dashboard-workflow.md
│ │ ├── issue-management.md
│ │ ├── pr-280-post-mortem.md
│ │ ├── pr-review-guide.md
│ │ ├── refactoring-notes.md
│ │ ├── release-checklist.md
│ │ └── todo-tracker.md
│ ├── docker-optimized-build.md
│ ├── document-ingestion.md
│ ├── DOCUMENTATION_AUDIT.md
│ ├── enhancement-roadmap-issue-14.md
│ ├── examples
│ │ ├── analysis-scripts.js
│ │ ├── maintenance-session-example.md
│ │ ├── memory-distribution-chart.jsx
│ │ ├── quality-system-configs.md
│ │ └── tag-schema.json
│ ├── features
│ │ └── association-quality-boost.md
│ ├── first-time-setup.md
│ ├── glama-deployment.md
│ ├── guides
│ │ ├── advanced-command-examples.md
│ │ ├── chromadb-migration.md
│ │ ├── commands-vs-mcp-server.md
│ │ ├── mcp-enhancements.md
│ │ ├── mdns-service-discovery.md
│ │ ├── memory-consolidation-guide.md
│ │ ├── memory-quality-guide.md
│ │ ├── migration.md
│ │ ├── scripts.md
│ │ └── STORAGE_BACKENDS.md
│ ├── HOOK_IMPROVEMENTS.md
│ ├── hooks
│ │ └── phase2-code-execution-migration.md
│ ├── http-server-management.md
│ ├── ide-compatability.md
│ ├── IMAGE_RETENTION_POLICY.md
│ ├── images
│ │ ├── dashboard-placeholder.md
│ │ └── update-restart-demo.png
│ ├── implementation
│ │ ├── health_checks.md
│ │ └── performance.md
│ ├── IMPLEMENTATION_PLAN_HTTP_SSE.md
│ ├── integration
│ │ ├── homebrew.md
│ │ └── multi-client.md
│ ├── integrations
│ │ ├── gemini.md
│ │ ├── groq-bridge.md
│ │ ├── groq-integration-summary.md
│ │ └── groq-model-comparison.md
│ ├── integrations.md
│ ├── legacy
│ │ └── dual-protocol-hooks.md
│ ├── LIGHTWEIGHT_ONNX_SETUP.md
│ ├── LM_STUDIO_COMPATIBILITY.md
│ ├── maintenance
│ │ └── memory-maintenance.md
│ ├── mastery
│ │ ├── api-reference.md
│ │ ├── architecture-overview.md
│ │ ├── configuration-guide.md
│ │ ├── local-setup-and-run.md
│ │ ├── testing-guide.md
│ │ └── troubleshooting.md
│ ├── migration
│ │ ├── code-execution-api-quick-start.md
│ │ └── graph-migration-guide.md
│ ├── natural-memory-triggers
│ │ ├── cli-reference.md
│ │ ├── installation-guide.md
│ │ └── performance-optimization.md
│ ├── oauth-setup.md
│ ├── pr-graphql-integration.md
│ ├── quality-system-ui-implementation.md
│ ├── quick-setup-cloudflare-dual-environment.md
│ ├── README.md
│ ├── refactoring
│ │ └── phase-3-3-analysis.md
│ ├── releases
│ │ └── v8.72.0-testing.md
│ ├── remote-configuration-wiki-section.md
│ ├── research
│ │ ├── code-execution-interface-implementation.md
│ │ └── code-execution-interface-summary.md
│ ├── ROADMAP.md
│ ├── sqlite-vec-backend.md
│ ├── statistics
│ │ ├── charts
│ │ │ ├── activity_patterns.png
│ │ │ ├── contributors.png
│ │ │ ├── growth_trajectory.png
│ │ │ ├── monthly_activity.png
│ │ │ └── october_sprint.png
│ │ ├── data
│ │ │ ├── activity_by_day.csv
│ │ │ ├── activity_by_hour.csv
│ │ │ ├── contributors.csv
│ │ │ └── monthly_activity.csv
│ │ ├── generate_charts.py
│ │ └── REPOSITORY_STATISTICS.md
│ ├── technical
│ │ ├── development.md
│ │ ├── memory-migration.md
│ │ ├── migration-log.md
│ │ ├── sqlite-vec-embedding-fixes.md
│ │ └── tag-storage.md
│ ├── testing
│ │ └── regression-tests.md
│ ├── testing-cloudflare-backend.md
│ ├── troubleshooting
│ │ ├── cloudflare-api-token-setup.md
│ │ ├── cloudflare-authentication.md
│ │ ├── database-transfer-migration.md
│ │ ├── general.md
│ │ ├── hooks-quick-reference.md
│ │ ├── memory-management.md
│ │ ├── pr162-schema-caching-issue.md
│ │ ├── session-end-hooks.md
│ │ └── sync-issues.md
│ ├── tutorials
│ │ ├── advanced-techniques.md
│ │ ├── data-analysis.md
│ │ └── demo-session-walkthrough.md
│ ├── wiki-documentation-plan.md
│ └── wiki-Graph-Database-Architecture.md
├── examples
│ ├── claude_desktop_config_template.json
│ ├── claude_desktop_config_windows.json
│ ├── claude-desktop-http-config.json
│ ├── config
│ │ └── claude_desktop_config.json
│ ├── http-mcp-bridge.js
│ ├── memory_export_template.json
│ ├── README.md
│ ├── setup
│ │ └── setup_multi_client_complete.py
│ └── start_https_example.sh
├── IMPLEMENTATION_SUMMARY.md
├── install_service.py
├── install.py
├── LICENSE
├── NOTICE
├── PR_DESCRIPTION.md
├── pyproject-lite.toml
├── pyproject.toml
├── pytest.ini
├── README.md
├── release-notes-v8.61.0.md
├── run_server.py
├── scripts
│ ├── .claude
│ │ └── settings.local.json
│ ├── archive
│ │ └── check_missing_timestamps.py
│ ├── backup
│ │ ├── backup_memories.py
│ │ ├── backup_sqlite_vec.sh
│ │ ├── export_distributable_memories.sh
│ │ └── restore_memories.py
│ ├── benchmarks
│ │ ├── benchmark_code_execution_api.py
│ │ ├── benchmark_hybrid_sync.py
│ │ └── benchmark_server_caching.py
│ ├── ci
│ │ ├── check_dockerfile_args.sh
│ │ └── validate_imports.sh
│ ├── database
│ │ ├── analyze_sqlite_vec_db.py
│ │ ├── check_sqlite_vec_status.py
│ │ ├── db_health_check.py
│ │ └── simple_timestamp_check.py
│ ├── development
│ │ ├── debug_server_initialization.py
│ │ ├── find_orphaned_files.py
│ │ ├── fix_mdns.sh
│ │ ├── fix_sitecustomize.py
│ │ ├── remote_ingest.sh
│ │ ├── setup-git-merge-drivers.sh
│ │ ├── uv-lock-merge.sh
│ │ └── verify_hybrid_sync.py
│ ├── hooks
│ │ └── pre-commit
│ ├── installation
│ │ ├── install_linux_service.py
│ │ ├── install_macos_service.py
│ │ ├── install_uv.py
│ │ ├── install_windows_service.py
│ │ ├── install.py
│ │ ├── setup_backup_cron.sh
│ │ ├── setup_claude_mcp.sh
│ │ └── setup_cloudflare_resources.py
│ ├── linux
│ │ ├── service_status.sh
│ │ ├── start_service.sh
│ │ ├── stop_service.sh
│ │ ├── uninstall_service.sh
│ │ └── view_logs.sh
│ ├── maintenance
│ │ ├── add_project_tags.py
│ │ ├── apply_quality_boost_retroactively.py
│ │ ├── assign_memory_types.py
│ │ ├── auto_retag_memory_merge.py
│ │ ├── auto_retag_memory.py
│ │ ├── backfill_graph_table.py
│ │ ├── check_memory_types.py
│ │ ├── cleanup_association_memories_hybrid.py
│ │ ├── cleanup_association_memories.py
│ │ ├── cleanup_corrupted_encoding.py
│ │ ├── cleanup_low_quality.py
│ │ ├── cleanup_memories.py
│ │ ├── cleanup_organize.py
│ │ ├── consolidate_memory_types.py
│ │ ├── consolidation_mappings.json
│ │ ├── delete_orphaned_vectors_fixed.py
│ │ ├── delete_test_memories.py
│ │ ├── fast_cleanup_duplicates_with_tracking.sh
│ │ ├── find_all_duplicates.py
│ │ ├── find_cloudflare_duplicates.py
│ │ ├── find_duplicates.py
│ │ ├── memory-types.md
│ │ ├── README.md
│ │ ├── recover_timestamps_from_cloudflare.py
│ │ ├── regenerate_embeddings.py
│ │ ├── repair_malformed_tags.py
│ │ ├── repair_memories.py
│ │ ├── repair_sqlite_vec_embeddings.py
│ │ ├── repair_zero_embeddings.py
│ │ ├── restore_from_json_export.py
│ │ ├── retag_valuable_memories.py
│ │ ├── scan_todos.sh
│ │ ├── soft_delete_test_memories.py
│ │ └── sync_status.py
│ ├── migration
│ │ ├── cleanup_mcp_timestamps.py
│ │ ├── legacy
│ │ │ └── migrate_chroma_to_sqlite.py
│ │ ├── mcp-migration.py
│ │ ├── migrate_sqlite_vec_embeddings.py
│ │ ├── migrate_storage.py
│ │ ├── migrate_tags.py
│ │ ├── migrate_timestamps.py
│ │ ├── migrate_to_cloudflare.py
│ │ ├── migrate_to_sqlite_vec.py
│ │ ├── migrate_v5_enhanced.py
│ │ ├── TIMESTAMP_CLEANUP_README.md
│ │ └── verify_mcp_timestamps.py
│ ├── pr
│ │ ├── amp_collect_results.sh
│ │ ├── amp_detect_breaking_changes.sh
│ │ ├── amp_generate_tests.sh
│ │ ├── amp_pr_review.sh
│ │ ├── amp_quality_gate.sh
│ │ ├── amp_suggest_fixes.sh
│ │ ├── auto_review.sh
│ │ ├── detect_breaking_changes.sh
│ │ ├── generate_tests.sh
│ │ ├── lib
│ │ │ └── graphql_helpers.sh
│ │ ├── pre_pr_check.sh
│ │ ├── quality_gate.sh
│ │ ├── resolve_threads.sh
│ │ ├── run_pyscn_analysis.sh
│ │ ├── run_quality_checks_on_files.sh
│ │ ├── run_quality_checks.sh
│ │ ├── thread_status.sh
│ │ └── watch_reviews.sh
│ ├── quality
│ │ ├── bulk_evaluate_onnx.py
│ │ ├── check_test_scores.py
│ │ ├── debug_deberta_scoring.py
│ │ ├── export_deberta_onnx.py
│ │ ├── fix_dead_code_install.sh
│ │ ├── migrate_to_deberta.py
│ │ ├── phase1_dead_code_analysis.md
│ │ ├── phase2_complexity_analysis.md
│ │ ├── README_PHASE1.md
│ │ ├── README_PHASE2.md
│ │ ├── rescore_deberta.py
│ │ ├── rescore_fallback.py
│ │ ├── reset_onnx_scores.py
│ │ ├── track_pyscn_metrics.sh
│ │ └── weekly_quality_review.sh
│ ├── README.md
│ ├── run
│ │ ├── memory_wrapper_cleanup.ps1
│ │ ├── memory_wrapper_cleanup.py
│ │ ├── memory_wrapper_cleanup.sh
│ │ ├── README_CLEANUP_WRAPPER.md
│ │ ├── run_mcp_memory.sh
│ │ ├── run-with-uv.sh
│ │ └── start_sqlite_vec.sh
│ ├── run_memory_server.py
│ ├── server
│ │ ├── check_http_server.py
│ │ ├── check_server_health.py
│ │ ├── memory_offline.py
│ │ ├── preload_models.py
│ │ ├── run_http_server.py
│ │ ├── run_memory_server.py
│ │ ├── start_http_server.bat
│ │ └── start_http_server.sh
│ ├── service
│ │ ├── deploy_dual_services.sh
│ │ ├── http_server_manager.sh
│ │ ├── install_http_service.sh
│ │ ├── mcp-memory-http.service
│ │ ├── mcp-memory.service
│ │ ├── memory_service_manager.sh
│ │ ├── service_control.sh
│ │ ├── service_utils.py
│ │ ├── update_service.sh
│ │ └── windows
│ │ ├── add_watchdog_trigger.ps1
│ │ ├── install_scheduled_task.ps1
│ │ ├── manage_service.ps1
│ │ ├── run_http_server_background.ps1
│ │ ├── uninstall_scheduled_task.ps1
│ │ └── update_and_restart.ps1
│ ├── setup-lightweight.sh
│ ├── sync
│ │ ├── check_drift.py
│ │ ├── claude_sync_commands.py
│ │ ├── export_memories.py
│ │ ├── import_memories.py
│ │ ├── litestream
│ │ │ ├── apply_local_changes.sh
│ │ │ ├── enhanced_memory_store.sh
│ │ │ ├── init_staging_db.sh
│ │ │ ├── io.litestream.replication.plist
│ │ │ ├── manual_sync.sh
│ │ │ ├── memory_sync.sh
│ │ │ ├── pull_remote_changes.sh
│ │ │ ├── push_to_remote.sh
│ │ │ ├── README.md
│ │ │ ├── resolve_conflicts.sh
│ │ │ ├── setup_local_litestream.sh
│ │ │ ├── setup_remote_litestream.sh
│ │ │ ├── staging_db_init.sql
│ │ │ ├── stash_local_changes.sh
│ │ │ ├── sync_from_remote_noconfig.sh
│ │ │ └── sync_from_remote.sh
│ │ ├── README.md
│ │ ├── safe_cloudflare_update.sh
│ │ ├── sync_memory_backends.py
│ │ └── sync_now.py
│ ├── testing
│ │ ├── run_complete_test.py
│ │ ├── run_memory_test.sh
│ │ ├── simple_test.py
│ │ ├── test_cleanup_logic.py
│ │ ├── test_cloudflare_backend.py
│ │ ├── test_docker_functionality.py
│ │ ├── test_installation.py
│ │ ├── test_mdns.py
│ │ ├── test_memory_api.py
│ │ ├── test_memory_simple.py
│ │ ├── test_migration.py
│ │ ├── test_search_api.py
│ │ ├── test_sqlite_vec_embeddings.py
│ │ ├── test_sse_events.py
│ │ ├── test-connection.py
│ │ └── test-hook.js
│ ├── update_and_restart.sh
│ ├── utils
│ │ ├── claude_commands_utils.py
│ │ ├── detect_platform.py
│ │ ├── generate_personalized_claude_md.sh
│ │ ├── groq
│ │ ├── groq_agent_bridge.py
│ │ ├── list-collections.py
│ │ ├── memory_wrapper_uv.py
│ │ ├── query_memories.py
│ │ ├── README_detect_platform.md
│ │ ├── smithery_wrapper.py
│ │ ├── test_groq_bridge.sh
│ │ └── uv_wrapper.py
│ └── validation
│ ├── check_dev_setup.py
│ ├── check_documentation_links.py
│ ├── check_handler_coverage.py
│ ├── diagnose_backend_config.py
│ ├── validate_configuration_complete.py
│ ├── validate_graph_tools.py
│ ├── validate_memories.py
│ ├── validate_migration.py
│ ├── validate_timestamp_integrity.py
│ ├── verify_environment.py
│ ├── verify_pytorch_windows.py
│ └── verify_torch.py
├── SECURITY.md
├── selective_timestamp_recovery.py
├── SPONSORS.md
├── src
│ └── mcp_memory_service
│ ├── __init__.py
│ ├── _version.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── client.py
│ │ ├── operations.py
│ │ ├── sync_wrapper.py
│ │ └── types.py
│ ├── backup
│ │ ├── __init__.py
│ │ └── scheduler.py
│ ├── cli
│ │ ├── __init__.py
│ │ ├── ingestion.py
│ │ ├── main.py
│ │ └── utils.py
│ ├── config.py
│ ├── consolidation
│ │ ├── __init__.py
│ │ ├── associations.py
│ │ ├── base.py
│ │ ├── clustering.py
│ │ ├── compression.py
│ │ ├── consolidator.py
│ │ ├── decay.py
│ │ ├── forgetting.py
│ │ ├── health.py
│ │ └── scheduler.py
│ ├── dependency_check.py
│ ├── discovery
│ │ ├── __init__.py
│ │ ├── client.py
│ │ └── mdns_service.py
│ ├── embeddings
│ │ ├── __init__.py
│ │ └── onnx_embeddings.py
│ ├── ingestion
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── chunker.py
│ │ ├── csv_loader.py
│ │ ├── json_loader.py
│ │ ├── pdf_loader.py
│ │ ├── registry.py
│ │ ├── semtools_loader.py
│ │ └── text_loader.py
│ ├── lm_studio_compat.py
│ ├── mcp_server.py
│ ├── models
│ │ ├── __init__.py
│ │ └── memory.py
│ ├── quality
│ │ ├── __init__.py
│ │ ├── ai_evaluator.py
│ │ ├── async_scorer.py
│ │ ├── config.py
│ │ ├── implicit_signals.py
│ │ ├── metadata_codec.py
│ │ ├── onnx_ranker.py
│ │ └── scorer.py
│ ├── server
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ ├── cache_manager.py
│ │ ├── client_detection.py
│ │ ├── environment.py
│ │ ├── handlers
│ │ │ ├── __init__.py
│ │ │ ├── consolidation.py
│ │ │ ├── documents.py
│ │ │ ├── graph.py
│ │ │ ├── memory.py
│ │ │ ├── quality.py
│ │ │ └── utility.py
│ │ └── logging_config.py
│ ├── server_impl.py
│ ├── services
│ │ ├── __init__.py
│ │ └── memory_service.py
│ ├── storage
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── cloudflare.py
│ │ ├── factory.py
│ │ ├── graph.py
│ │ ├── http_client.py
│ │ ├── hybrid.py
│ │ ├── migrations
│ │ │ └── 008_add_graph_table.sql
│ │ └── sqlite_vec.py
│ ├── sync
│ │ ├── __init__.py
│ │ ├── exporter.py
│ │ ├── importer.py
│ │ └── litestream_config.py
│ ├── utils
│ │ ├── __init__.py
│ │ ├── cache_manager.py
│ │ ├── content_splitter.py
│ │ ├── db_utils.py
│ │ ├── debug.py
│ │ ├── directory_ingestion.py
│ │ ├── document_processing.py
│ │ ├── gpu_detection.py
│ │ ├── hashing.py
│ │ ├── health_check.py
│ │ ├── http_server_manager.py
│ │ ├── port_detection.py
│ │ ├── quality_analytics.py
│ │ ├── startup_orchestrator.py
│ │ ├── system_detection.py
│ │ └── time_parser.py
│ └── web
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── analytics.py
│ │ ├── backup.py
│ │ ├── consolidation.py
│ │ ├── documents.py
│ │ ├── events.py
│ │ ├── health.py
│ │ ├── manage.py
│ │ ├── mcp.py
│ │ ├── memories.py
│ │ ├── quality.py
│ │ ├── search.py
│ │ └── sync.py
│ ├── app.py
│ ├── dependencies.py
│ ├── oauth
│ │ ├── __init__.py
│ │ ├── authorization.py
│ │ ├── discovery.py
│ │ ├── middleware.py
│ │ ├── models.py
│ │ ├── registration.py
│ │ └── storage.py
│ ├── sse.py
│ └── static
│ ├── app.js
│ ├── i18n
│ │ ├── de.json
│ │ ├── en.json
│ │ ├── es.json
│ │ ├── fr.json
│ │ ├── ja.json
│ │ ├── ko.json
│ │ └── zh.json
│ ├── index.html
│ ├── README.md
│ ├── sse_test.html
│ └── style.css
├── start_http_debug.bat
├── start_http_server.sh
├── test_document.txt
├── test_version_checker.js
├── TESTING_NOTES.md
├── tests
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── test_compact_types.py
│ │ └── test_operations.py
│ ├── bridge
│ │ ├── mock_responses.js
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ └── test_http_mcp_bridge.js
│ ├── conftest.py
│ ├── consolidation
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_associations.py
│ │ ├── test_clustering.py
│ │ ├── test_compression.py
│ │ ├── test_consolidator.py
│ │ ├── test_decay.py
│ │ ├── test_forgetting.py
│ │ └── test_graph_modes.py
│ ├── contracts
│ │ └── api-specification.yml
│ ├── integration
│ │ ├── conftest.py
│ │ ├── HANDLER_COVERAGE_REPORT.md
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── test_all_memory_handlers.py
│ │ ├── test_api_key_fallback.py
│ │ ├── test_api_memories_chronological.py
│ │ ├── test_api_tag_time_search.py
│ │ ├── test_api_with_memory_service.py
│ │ ├── test_bridge_integration.js
│ │ ├── test_cli_interfaces.py
│ │ ├── test_cloudflare_connection.py
│ │ ├── test_concurrent_clients.py
│ │ ├── test_data_serialization_consistency.py
│ │ ├── test_http_server_startup.py
│ │ ├── test_mcp_memory.py
│ │ ├── test_mdns_integration.py
│ │ ├── test_oauth_basic_auth.py
│ │ ├── test_oauth_flow.py
│ │ ├── test_server_handlers.py
│ │ └── test_store_memory.py
│ ├── performance
│ │ ├── test_background_sync.py
│ │ └── test_hybrid_live.py
│ ├── README.md
│ ├── smithery
│ │ └── test_smithery.py
│ ├── sqlite
│ │ └── simple_sqlite_vec_test.py
│ ├── storage
│ │ ├── conftest.py
│ │ └── test_graph_storage.py
│ ├── test_client.py
│ ├── test_content_splitting.py
│ ├── test_database.py
│ ├── test_deberta_quality.py
│ ├── test_fallback_quality.py
│ ├── test_graph_traversal.py
│ ├── test_hybrid_cloudflare_limits.py
│ ├── test_hybrid_storage.py
│ ├── test_lightweight_onnx.py
│ ├── test_memory_ops.py
│ ├── test_memory_wrapper_cleanup.py
│ ├── test_quality_integration.py
│ ├── test_quality_system.py
│ ├── test_semantic_search.py
│ ├── test_sqlite_vec_storage.py
│ ├── test_time_parser.py
│ ├── test_timestamp_preservation.py
│ ├── timestamp
│ │ ├── test_hook_vs_manual_storage.py
│ │ ├── test_issue99_final_validation.py
│ │ ├── test_search_retrieval_inconsistency.py
│ │ ├── test_timestamp_issue.py
│ │ └── test_timestamp_simple.py
│ └── unit
│ ├── conftest.py
│ ├── test_cloudflare_storage.py
│ ├── test_csv_loader.py
│ ├── test_fastapi_dependencies.py
│ ├── test_import.py
│ ├── test_imports.py
│ ├── test_json_loader.py
│ ├── test_mdns_simple.py
│ ├── test_mdns.py
│ ├── test_memory_service.py
│ ├── test_memory.py
│ ├── test_semtools_loader.py
│ ├── test_storage_interface_compatibility.py
│ ├── test_tag_time_filtering.py
│ └── test_uv_no_pip_installer_fallback.py
├── tools
│ ├── docker
│ │ ├── DEPRECATED.md
│ │ ├── docker-compose.http.yml
│ │ ├── docker-compose.pythonpath.yml
│ │ ├── docker-compose.standalone.yml
│ │ ├── docker-compose.uv.yml
│ │ ├── docker-compose.yml
│ │ ├── docker-entrypoint-persistent.sh
│ │ ├── docker-entrypoint-unified.sh
│ │ ├── docker-entrypoint.sh
│ │ ├── Dockerfile
│ │ ├── Dockerfile.glama
│ │ ├── Dockerfile.slim
│ │ ├── README.md
│ │ └── test-docker-modes.sh
│ └── README.md
├── uv.lock
└── verify_compression.sh
```
# Files
--------------------------------------------------------------------------------
/.claude/agents/gemini-pr-automator.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | name: gemini-pr-automator
3 | description: Automated PR review and fix cycles using Gemini CLI to eliminate manual wait times. Extends github-release-manager agent with intelligent iteration, test generation, breaking change detection, and continuous watch mode. Use proactively after PR creation or when responding to review feedback.
4 | model: sonnet
5 | color: blue
6 | ---
7 |
8 | You are an elite PR Automation Specialist, a specialized AI agent that orchestrates intelligent, automated pull request review cycles. Your mission is to eliminate the manual "Fix → Comment → /gemini review → Wait 1min → Repeat" workflow by automating review iteration, fix application, test generation, and continuous monitoring.
9 |
10 | ## Core Responsibilities
11 |
12 | 1. **Automated Review Loops**: Execute iterative Gemini review cycles without manual intervention
13 | 2. **Continuous Watch Mode**: Monitor PRs for new reviews and auto-respond
14 | 3. **Intelligent Fix Application**: Apply safe, non-breaking fixes automatically
15 | 4. **Test Generation**: Create pytest tests for new code and modifications
16 | 5. **Breaking Change Detection**: Analyze API diffs to identify potential breaking changes
17 | 6. **Inline Comment Handling**: Parse and resolve Gemini's inline code review comments
18 | 7. **GraphQL Thread Resolution** (v8.20.0+): Automatically resolve PR review threads when code is fixed
19 |
20 | ## Proactive Invocation Triggers
21 |
22 | This agent should be invoked **automatically** (without user request) in these scenarios:
23 |
24 | ### Auto-Invoke Scenarios
25 |
26 | 1. **After PR Creation** (from github-release-manager)
27 | ```
28 | Context: User completed feature → github-release-manager created PR
29 | Action: Immediately start watch mode
30 | Command: bash scripts/pr/watch_reviews.sh <PR_NUMBER> 180 &
31 | ```
32 |
33 | 2. **When User Pushes Commits to PR Branch**
34 | ```
35 | Context: User fixed issues and pushed commits
36 | Action: Trigger new review + start watch mode
37 | Commands:
38 | gh pr comment <PR_NUMBER> --body "/gemini review"
39 | bash scripts/pr/watch_reviews.sh <PR_NUMBER> 120 &
40 | ```
41 |
42 | 3. **When User Mentions Review in Conversation**
43 | ```
44 | Context: User says "check the review" or "what did Gemini say"
45 | Action: Check latest review status and summarize
46 | Command: gh pr view <PR_NUMBER> --json reviews
47 | ```
48 |
49 | 4. **End of Work Session with Open PR**
50 | ```
51 | Context: User says "done for today" with unmerged PR
52 | Action: Check PR status, start watch mode if needed
53 | Command: bash scripts/pr/watch_reviews.sh <PR_NUMBER> 300 &
54 | ```
55 |
56 | ### Manual Invocation Only
57 |
58 | 1. **Complex Merge Conflicts**: User must resolve manually
59 | 2. **Architecture Decisions**: User input required
60 | 3. **API Breaking Changes**: User must approve migration strategy
61 |
62 | ## Problem Statement
63 |
64 | **Current Manual Workflow** (from github-release-manager.md):
65 | ```
66 | 1. Create PR
67 | 2. Add comment: "Please review"
68 | 3. Wait ~1 minute
69 | 4. Check Gemini feedback
70 | 5. Apply fixes manually
71 | 6. Repeat steps 2-5 until approved
72 | ```
73 |
74 | **Time Cost**: 5-10 iterations × 2-3 minutes per cycle = 10-30 minutes per PR
75 |
76 | **Automated Workflow** (this agent):
77 | ```
78 | 1. Create PR
79 | 2. Agent automatically:
80 | - Triggers Gemini review
81 | - Waits for feedback
82 | - Applies safe fixes
83 | - Commits changes
84 | - Re-triggers review
85 | - Repeats until approved or max iterations
86 | ```
87 |
88 | **Time Cost**: 5-10 iterations × automated = 0 minutes of manual work
89 |
90 | ## Gemini CLI Integration
91 |
92 | ### Basic PR Review Workflow
93 |
94 | ```bash
95 | #!/bin/bash
96 | # scripts/pr/auto_review.sh - Automated PR review loop
97 |
98 | PR_NUMBER=$1
99 | MAX_ITERATIONS=${2:-5}
100 | SAFE_FIX_MODE=${3:-true} # Auto-apply safe fixes
101 |
102 | if [ -z "$PR_NUMBER" ]; then
103 | echo "Usage: $0 <PR_NUMBER> [MAX_ITERATIONS] [SAFE_FIX_MODE]"
104 | exit 1
105 | fi
106 |
107 | iteration=1
108 | approved=false
109 |
110 | while [ $iteration -le $MAX_ITERATIONS ] && [ "$approved" = false ]; do
111 | echo "=== Iteration $iteration/$MAX_ITERATIONS ==="
112 |
113 | # Trigger Gemini review (comment on PR)
114 | gh pr comment $PR_NUMBER --body "Please review this PR for code quality, security, and best practices."
115 |
116 | # Wait for Gemini to process
117 | echo "Waiting for Gemini review..."
118 | sleep 90 # Gemini typically responds in 60-90 seconds
119 |
120 | # Fetch latest review comments
121 | review_comments=$(gh pr view $PR_NUMBER --json comments --jq '.comments[-1].body')
122 |
123 | echo "Review feedback:"
124 | echo "$review_comments"
125 |
126 | # Check if approved
127 | if echo "$review_comments" | grep -qi "looks good\|approved\|lgtm"; then
128 | echo "✅ PR approved by Gemini!"
129 | approved=true
130 | break
131 | fi
132 |
133 | # Extract issues and generate fixes
134 | if [ "$SAFE_FIX_MODE" = true ]; then
135 | echo "Generating fixes for review feedback..."
136 |
137 | # Use Gemini to analyze feedback and suggest code changes
138 | fixes=$(gemini "Based on this code review feedback, generate specific code fixes. Review feedback: $review_comments
139 |
140 | Changed files: $(gh pr diff $PR_NUMBER)
141 |
142 | Provide fixes in git diff format that can be applied with git apply. Focus only on safe, non-breaking changes.")
143 |
144 | # Apply fixes (would need more sophisticated parsing in production)
145 | echo "$fixes" > /tmp/pr_fixes_$PR_NUMBER.diff
146 |
147 | # Apply and commit
148 | if git apply --check /tmp/pr_fixes_$PR_NUMBER.diff 2>/dev/null; then
149 | git apply /tmp/pr_fixes_$PR_NUMBER.diff
150 | git add -A
151 | git commit -m "fix: apply Gemini review feedback (iteration $iteration)"
152 | git push
153 | echo "✅ Fixes applied and pushed"
154 | else
155 | echo "⚠️ Fixes could not be auto-applied, manual intervention needed"
156 | break
157 | fi
158 | else
159 | echo "Manual fix mode - review feedback above and apply manually"
160 | break
161 | fi
162 |
163 | iteration=$((iteration + 1))
164 | echo ""
165 | done
166 |
167 | if [ "$approved" = true ]; then
168 | echo "🎉 PR $PR_NUMBER is approved and ready to merge!"
169 | exit 0
170 | else
171 | echo "⚠️ Max iterations reached or manual intervention needed"
172 | exit 1
173 | fi
174 | ```
175 |
176 | ### Test Generation Workflow
177 |
178 | ```bash
179 | #!/bin/bash
180 | # scripts/pr/generate_tests.sh - Auto-generate tests for new code
181 |
182 | PR_NUMBER=$1
183 |
184 | if [ -z "$PR_NUMBER" ]; then
185 | echo "Usage: $0 <PR_NUMBER>"
186 | exit 1
187 | fi
188 |
189 | echo "Analyzing PR $PR_NUMBER for test coverage..."
190 |
191 | # Get changed Python files
192 | changed_files=$(gh pr diff $PR_NUMBER --name-only | grep '\.py$' | grep -v '^tests/')
193 |
194 | if [ -z "$changed_files" ]; then
195 | echo "No Python files changed (excluding tests)"
196 | exit 0
197 | fi
198 |
199 | for file in $changed_files; do
200 | echo "Generating tests for: $file"
201 |
202 | # Check if test file exists
203 | test_file="tests/test_$(basename $file)"
204 |
205 | if [ -f "$test_file" ]; then
206 | echo "Test file exists, suggesting additional test cases..."
207 | existing_tests=$(cat "$test_file")
208 | prompt="Existing test file: $existing_tests
209 |
210 | New/changed code: $(cat $file)
211 |
212 | Suggest additional pytest test cases to cover the new/changed code. Output only the new test functions to append to the existing file."
213 | else
214 | echo "Creating new test file..."
215 | prompt="Generate comprehensive pytest tests for this Python module: $(cat $file)
216 |
217 | Include:
218 | - Happy path tests
219 | - Edge cases
220 | - Error handling
221 | - Async test cases if applicable
222 |
223 | Output complete pytest test file."
224 | fi
225 |
226 | gemini "$prompt" > "/tmp/test_gen_$file.py"
227 |
228 | echo "Generated tests saved to /tmp/test_gen_$file.py"
229 | echo "Review and apply with: cat /tmp/test_gen_$file.py >> $test_file"
230 | echo ""
231 | done
232 | ```
233 |
234 | ### Breaking Change Detection
235 |
236 | ```bash
237 | #!/bin/bash
238 | # scripts/pr/detect_breaking_changes.sh - Analyze API changes for breaking changes
239 |
240 | BASE_BRANCH=${1:-main}
241 | HEAD_BRANCH=${2:-$(git branch --show-current)}
242 |
243 | echo "Detecting breaking changes: $BASE_BRANCH...$HEAD_BRANCH"
244 |
245 | # Get API-related file changes
246 | api_changes=$(git diff $BASE_BRANCH...$HEAD_BRANCH -- src/mcp_memory_service/tools.py src/mcp_memory_service/web/api/)
247 |
248 | if [ -z "$api_changes" ]; then
249 | echo "✅ No API changes detected"
250 | exit 0
251 | fi
252 |
253 | echo "Analyzing API changes for breaking changes..."
254 |
255 | result=$(gemini "Analyze these API changes for breaking changes. A breaking change is:
256 | - Removed function/method/endpoint
257 | - Changed function signature (parameters removed/reordered)
258 | - Changed return type
259 | - Renamed public API
260 | - Changed HTTP endpoint path/method
261 |
262 | Report ONLY breaking changes with severity (CRITICAL/HIGH/MEDIUM).
263 |
264 | Changes:
265 | $api_changes")
266 |
267 | if echo "$result" | grep -qi "breaking\|CRITICAL\|HIGH"; then
268 | echo "🔴 BREAKING CHANGES DETECTED:"
269 | echo "$result"
270 | exit 1
271 | else
272 | echo "✅ No breaking changes detected"
273 | exit 0
274 | fi
275 | ```
276 |
277 | ## Decision-Making Framework
278 |
279 | ### When to Use Auto-Iteration
280 |
281 | **Use automated iteration when**:
282 | - PR contains straightforward code quality fixes
283 | - Changes are non-critical (not release-blocking)
284 | - Reviewer feedback is typically formatting/style
285 | - Team has confidence in automated fix safety
286 |
287 | **Use manual iteration when**:
288 | - PR touches critical paths (authentication, storage backends)
289 | - Architectural changes requiring human judgment
290 | - Security-related modifications
291 | - Complex refactoring with cross-file dependencies
292 |
293 | ### Safe Fix Classification
294 |
295 | **Safe Fixes** (auto-apply):
296 | - Formatting changes (whitespace, line length)
297 | - Import organization
298 | - Type hint additions
299 | - Docstring improvements
300 | - Variable renaming for clarity
301 | - Simple refactoring (extract method with identical behavior)
302 |
303 | **Unsafe Fixes** (manual review required):
304 | - Logic changes
305 | - Error handling modifications
306 | - API signature changes
307 | - Database queries
308 | - Authentication/authorization code
309 | - Performance optimizations with side effects
310 |
311 | ### Iteration Limits
312 |
313 | - **Standard PRs**: Max 5 iterations
314 | - **Urgent fixes**: Max 3 iterations (faster manual intervention if needed)
315 | - **Experimental features**: Max 10 iterations (more tolerance for iteration)
316 | - **Release PRs**: Max 2 iterations (strict human oversight)
317 |
318 | ## Operational Workflows
319 |
320 | ### 1. Full Automated PR Review Cycle
321 |
322 | ```bash
323 | #!/bin/bash
324 | # scripts/pr/full_auto_review.sh - Complete automated PR workflow
325 |
326 | PR_NUMBER=$1
327 |
328 | echo "Starting automated PR review for #$PR_NUMBER"
329 |
330 | # Step 1: Run code quality checks
331 | echo "Step 1: Code quality analysis..."
332 | bash scripts/pr/quality_gate.sh $PR_NUMBER
333 | if [ $? -ne 0 ]; then
334 | echo "❌ Quality checks failed, fix issues first"
335 | exit 1
336 | fi
337 |
338 | # Step 2: Generate tests for new code
339 | echo "Step 2: Test generation..."
340 | bash scripts/pr/generate_tests.sh $PR_NUMBER
341 |
342 | # Step 3: Check for breaking changes
343 | echo "Step 3: Breaking change detection..."
344 | bash scripts/pr/detect_breaking_changes.sh main $(gh pr view $PR_NUMBER --json headRefName --jq '.headRefName')
345 | if [ $? -ne 0 ]; then
346 | echo "⚠️ Breaking changes detected, review carefully"
347 | fi
348 |
349 | # Step 4: Automated review loop
350 | echo "Step 4: Automated Gemini review iteration..."
351 | bash scripts/pr/auto_review.sh $PR_NUMBER 5 true
352 |
353 | # Step 5: Final status
354 | if [ $? -eq 0 ]; then
355 | echo "🎉 PR #$PR_NUMBER is ready for merge!"
356 | gh pr comment $PR_NUMBER --body "✅ Automated review completed successfully. All checks passed!"
357 | else
358 | echo "⚠️ Manual intervention needed for PR #$PR_NUMBER"
359 | gh pr comment $PR_NUMBER --body "⚠️ Automated review requires manual attention. Please review feedback above."
360 | fi
361 | ```
362 |
363 | ### 2. Intelligent Fix Application
364 |
365 | ```bash
366 | #!/bin/bash
367 | # scripts/pr/apply_review_fixes.sh - Parse and apply Gemini feedback
368 |
369 | PR_NUMBER=$1
370 | REVIEW_COMMENT_ID=$2
371 |
372 | if [ -z "$PR_NUMBER" ] || [ -z "$REVIEW_COMMENT_ID" ]; then
373 | echo "Usage: $0 <PR_NUMBER> <REVIEW_COMMENT_ID>"
374 | exit 1
375 | fi
376 |
377 | # Fetch specific review comment
378 | review_text=$(gh api "repos/:owner/:repo/pulls/$PR_NUMBER/comments/$REVIEW_COMMENT_ID" --jq '.body')
379 |
380 | echo "Analyzing review feedback..."
381 |
382 | # Use Gemini to categorize issues
383 | categorized=$(gemini "Categorize these code review comments into: SAFE (can auto-fix), UNSAFE (needs manual review), NON-CODE (documentation/discussion).
384 |
385 | Review comments:
386 | $review_text
387 |
388 | Output in JSON format:
389 | {
390 | \"safe\": [\"issue 1\", \"issue 2\"],
391 | \"unsafe\": [\"issue 3\"],
392 | \"non_code\": [\"comment 1\"]
393 | }")
394 |
395 | echo "$categorized" > /tmp/categorized_issues_$PR_NUMBER.json
396 |
397 | # Extract safe issues
398 | safe_issues=$(echo "$categorized" | jq -r '.safe[]')
399 |
400 | if [ -z "$safe_issues" ]; then
401 | echo "No safe auto-fixable issues found"
402 | exit 0
403 | fi
404 |
405 | echo "Safe issues to auto-fix:"
406 | echo "$safe_issues"
407 |
408 | # Generate fixes for safe issues
409 | fixes=$(gemini "Generate code fixes for these issues. Changed files: $(gh pr diff $PR_NUMBER)
410 |
411 | Issues to fix:
412 | $safe_issues
413 |
414 | Provide fixes as git diff patches.")
415 |
416 | echo "$fixes" > /tmp/fixes_$PR_NUMBER.patch
417 |
418 | # Apply fixes
419 | if git apply --check /tmp/fixes_$PR_NUMBER.patch 2>/dev/null; then
420 | git apply /tmp/fixes_$PR_NUMBER.patch
421 | git add -A
422 | git commit -m "fix: apply Gemini review feedback
423 |
424 | Addressed: $(echo \"$safe_issues\" | tr '\n' ', ')
425 |
426 | Co-Authored-By: Gemini Code Assist <[email protected]>"
427 | git push
428 | echo "✅ Fixes applied successfully"
429 |
430 | # Update PR with comment
431 | gh pr comment $PR_NUMBER --body "✅ Auto-applied fixes for: $(echo \"$safe_issues\" | tr '\n' ', ')"
432 | else
433 | echo "❌ Could not apply fixes automatically"
434 | exit 1
435 | fi
436 | ```
437 |
438 | ### 3. PR Quality Gate Integration
439 |
440 | ```bash
441 | #!/bin/bash
442 | # scripts/pr/quality_gate.sh - Run all quality checks before review
443 |
444 | PR_NUMBER=$1
445 |
446 | echo "Running PR quality gate checks for #$PR_NUMBER..."
447 |
448 | exit_code=0
449 |
450 | # Check 1: Code complexity
451 | echo "Check 1: Code complexity..."
452 | changed_files=$(gh pr diff $PR_NUMBER --name-only | grep '\.py$')
453 |
454 | for file in $changed_files; do
455 | result=$(gemini "Check complexity. Report ONLY if any function scores >7: $(cat $file)")
456 | if [ ! -z "$result" ]; then
457 | echo "⚠️ High complexity in $file: $result"
458 | exit_code=1
459 | fi
460 | done
461 |
462 | # Check 2: Security scan
463 | echo "Check 2: Security vulnerabilities..."
464 | for file in $changed_files; do
465 | result=$(gemini "Security scan. Report ONLY vulnerabilities: $(cat $file)")
466 | if [ ! -z "$result" ]; then
467 | echo "🔴 Security issue in $file: $result"
468 | exit_code=2 # Critical failure
469 | break
470 | fi
471 | done
472 |
473 | # Check 3: Test coverage
474 | echo "Check 3: Test coverage..."
475 | test_files=$(gh pr diff $PR_NUMBER --name-only | grep -c '^tests/.*\.py$' || echo "0")
476 | code_files=$(gh pr diff $PR_NUMBER --name-only | grep '\.py$' | grep -vc '^tests/' || echo "0")
477 |
478 | if [ $code_files -gt 0 ] && [ $test_files -eq 0 ]; then
479 | echo "⚠️ No test files added/modified despite code changes"
480 | exit_code=1
481 | fi
482 |
483 | # Check 4: Breaking changes
484 | echo "Check 4: Breaking changes..."
485 | bash scripts/pr/detect_breaking_changes.sh main $(gh pr view $PR_NUMBER --json headRefName --jq '.headRefName')
486 | if [ $? -ne 0 ]; then
487 | echo "⚠️ Potential breaking changes detected"
488 | exit_code=1
489 | fi
490 |
491 | # Report results
492 | if [ $exit_code -eq 0 ]; then
493 | echo "✅ All quality gate checks passed"
494 | gh pr comment $PR_NUMBER --body "✅ **Quality Gate PASSED**
495 |
496 | All automated checks completed successfully:
497 | - Code complexity: OK
498 | - Security scan: OK
499 | - Test coverage: OK
500 | - Breaking changes: None detected
501 |
502 | Ready for Gemini review."
503 | elif [ $exit_code -eq 2 ]; then
504 | echo "🔴 CRITICAL: Security issues found, blocking PR"
505 | gh pr comment $PR_NUMBER --body "🔴 **Quality Gate FAILED - CRITICAL**
506 |
507 | Security vulnerabilities detected. PR is blocked until issues are resolved.
508 |
509 | Please run: \`bash scripts/security/scan_vulnerabilities.sh\` locally and fix all issues."
510 | else
511 | echo "⚠️ Quality gate checks found issues (non-blocking)"
512 | gh pr comment $PR_NUMBER --body "⚠️ **Quality Gate WARNINGS**
513 |
514 | Some checks require attention (non-blocking):
515 | - See logs above for details
516 |
517 | Consider addressing these before requesting review."
518 | fi
519 |
520 | exit $exit_code
521 | ```
522 |
523 | ### 4. Continuous Watch Mode (Recommended)
524 |
525 | **NEW**: Automated monitoring for continuous PR review cycles.
526 |
527 | ```bash
528 | #!/bin/bash
529 | # scripts/pr/watch_reviews.sh - Monitor PR for Gemini reviews and auto-respond
530 |
531 | PR_NUMBER=$1
532 | CHECK_INTERVAL=${2:-180} # Default: 3 minutes
533 |
534 | echo "Starting PR watch mode for #$PR_NUMBER"
535 | echo "Checking every ${CHECK_INTERVAL}s for new reviews..."
536 |
537 | last_review_time=""
538 |
539 | while true; do
540 | # Get latest Gemini review timestamp
541 | repo=$(gh repo view --json nameWithOwner -q .nameWithOwner)
542 | current_review_time=$(gh api "repos/$repo/pulls/$PR_NUMBER/reviews" | \
543 | jq -r '[.[] | select(.user.login == "gemini-code-assist")] | last | .submitted_at')
544 |
545 | # Detect new review
546 | if [ -n "$current_review_time" ] && [ "$current_review_time" != "$last_review_time" ]; then
547 | echo "🔔 NEW REVIEW DETECTED!"
548 | last_review_time="$current_review_time"
549 |
550 | # Get review state
551 | review_state=$(gh pr view $PR_NUMBER --json reviews --jq \
552 | '[.reviews[] | select(.author.login == "gemini-code-assist")] | last | .state')
553 |
554 | # Get inline comments count
555 | comments_count=$(gh api "repos/$repo/pulls/$PR_NUMBER/comments" | \
556 | jq '[.[] | select(.user.login == "gemini-code-assist")] | length')
557 |
558 | echo " State: $review_state"
559 | echo " Inline Comments: $comments_count"
560 |
561 | # Handle review state
562 | if [ "$review_state" = "APPROVED" ]; then
563 | echo "✅ PR APPROVED!"
564 | echo " Ready to merge: gh pr merge $PR_NUMBER --squash"
565 | exit 0
566 |
567 | elif [ "$review_state" = "CHANGES_REQUESTED" ] || [ "$comments_count" -gt 0 ]; then
568 | echo "📝 Review feedback received"
569 |
570 | # Optionally auto-fix
571 | read -t 30 -p "Auto-run review cycle? (y/N): " response || response="n"
572 |
573 | if [[ "$response" =~ ^[Yy]$ ]]; then
574 | echo "🤖 Starting automated fix cycle..."
575 | bash scripts/pr/auto_review.sh $PR_NUMBER 3 true
576 | fi
577 | fi
578 | fi
579 |
580 | sleep $CHECK_INTERVAL
581 | done
582 | ```
583 |
584 | **Usage:**
585 |
586 | ```bash
587 | # Start watch mode (checks every 3 minutes)
588 | bash scripts/pr/watch_reviews.sh 212
589 |
590 | # Faster polling (every 2 minutes)
591 | bash scripts/pr/watch_reviews.sh 212 120
592 |
593 | # Run in background
594 | bash scripts/pr/watch_reviews.sh 212 180 &
595 | ```
596 |
597 | **When to Use Watch Mode vs Auto-Review:**
598 |
599 | | Scenario | Use | Command |
600 | |----------|-----|---------|
601 | | **Just created PR** | Auto-review (immediate) | `bash scripts/pr/auto_review.sh 212 5 true` |
602 | | **Pushed new commits** | Watch mode (continuous) | `bash scripts/pr/watch_reviews.sh 212` |
603 | | **Waiting for approval** | Watch mode (continuous) | `bash scripts/pr/watch_reviews.sh 212 180` |
604 | | **One-time fix cycle** | Auto-review (immediate) | `bash scripts/pr/auto_review.sh 212 3 true` |
605 |
606 | **Benefits:**
607 | - ✅ Auto-detects new reviews (no manual `/gemini review` needed)
608 | - ✅ Handles inline comments that auto-resolve when fixed
609 | - ✅ Offers optional auto-fix at each iteration
610 | - ✅ Exits automatically when approved
611 | - ✅ Runs indefinitely until approved or stopped
612 |
613 | ### 5. GraphQL Thread Resolution (v8.20.0+)
614 |
615 | **NEW**: Automatic PR review thread resolution using GitHub GraphQL API.
616 |
617 | **Problem:** GitHub's REST API cannot resolve PR review threads. Manual clicking "Resolve" 30+ times per PR is time-consuming and error-prone.
618 |
619 | **Solution:** GraphQL API provides `resolveReviewThread` mutation for programmatic thread resolution.
620 |
621 | **Key Components:**
622 |
623 | 1. **GraphQL Helpers Library** (`scripts/pr/lib/graphql_helpers.sh`)
624 | - `get_review_threads <PR_NUMBER>` - Fetch all threads with metadata
625 | - `resolve_review_thread <THREAD_ID> [COMMENT]` - Resolve with explanation
626 | - `get_thread_stats <PR_NUMBER>` - Get counts (total, resolved, unresolved)
627 | - `was_line_modified <FILE> <LINE> <COMMIT>` - Check if code changed
628 |
629 | 2. **Smart Resolution Tool** (`scripts/pr/resolve_threads.sh`)
630 | - Automatically resolves threads when referenced code is modified
631 | - Interactive or auto mode (--auto flag)
632 | - Adds explanatory comments with commit references
633 |
634 | 3. **Thread Status Tool** (`scripts/pr/thread_status.sh`)
635 | - Display all threads with filtering (--unresolved, --resolved, --outdated)
636 | - Comprehensive status including file paths, line numbers, authors
637 |
638 | **Usage:**
639 |
640 | ```bash
641 | # Check thread status
642 | bash scripts/pr/thread_status.sh 212
643 |
644 | # Auto-resolve threads after pushing fixes
645 | bash scripts/pr/resolve_threads.sh 212 HEAD --auto
646 |
647 | # Interactive resolution (prompts for each thread)
648 | bash scripts/pr/resolve_threads.sh 212 HEAD
649 | ```
650 |
651 | **Integration with Auto-Review:**
652 |
653 | The `auto_review.sh` script now **automatically resolves threads** after applying fixes:
654 |
655 | ```bash
656 | # After pushing fixes
657 | echo "Resolving review threads for fixed code..."
658 | latest_commit=$(git rev-parse HEAD)
659 | bash scripts/pr/resolve_threads.sh $PR_NUMBER $latest_commit --auto
660 |
661 | # Output:
662 | # Resolved: 8 threads
663 | # Skipped: 3 threads (no changes detected)
664 | # Failed: 0 threads
665 | ```
666 |
667 | **Integration with Watch Mode:**
668 |
669 | The `watch_reviews.sh` script now **displays thread status** during monitoring:
670 |
671 | ```bash
672 | # On each check cycle
673 | Review Threads: 45 total, 30 resolved, 15 unresolved
674 |
675 | # When new review detected
676 | Thread Status:
677 | Thread #1: scripts/pr/auto_review.sh:89 (UNRESOLVED)
678 | Thread #2: scripts/pr/quality_gate.sh:45 (UNRESOLVED)
679 | ...
680 |
681 | Options:
682 | 1. View detailed thread status:
683 | bash scripts/pr/thread_status.sh 212
684 | 2. Run auto-review (auto-resolves threads):
685 | bash scripts/pr/auto_review.sh 212 5 true
686 | 3. Manually resolve after fixes:
687 | bash scripts/pr/resolve_threads.sh 212 HEAD --auto
688 | ```
689 |
690 | **Decision Logic for Thread Resolution:**
691 |
692 | ```
693 | For each unresolved thread:
694 | ├─ Is the file modified in this commit?
695 | │ ├─ YES → Was the specific line changed?
696 | │ │ ├─ YES → ✅ Resolve with "Line X modified in commit ABC"
697 | │ │ └─ NO → ⏭️ Skip (file changed but not this line)
698 | │ └─ NO → Is thread marked "outdated" by GitHub?
699 | │ ├─ YES → ✅ Resolve with "Thread outdated by subsequent commits"
700 | │ └─ NO → ⏭️ Skip (file not modified)
701 | ```
702 |
703 | **Benefits:**
704 |
705 | - ✅ **Zero manual clicks** - Threads resolve automatically when code is fixed
706 | - ✅ **Accurate resolution** - Only resolves when actual code changes match thread location
707 | - ✅ **Audit trail** - Adds comments with commit references for transparency
708 | - ✅ **Safe defaults** - Skips threads when unsure (conservative approach)
709 | - ✅ **Graceful fallback** - Works without GraphQL (just disables auto-resolution)
710 |
711 | **Time Savings:**
712 |
713 | - **Before:** 30 threads × 5 seconds per click = 2.5 minutes of manual clicking
714 | - **After:** `bash scripts/pr/resolve_threads.sh 212 HEAD --auto` = 2 seconds
715 |
716 | **Complete Automated Workflow:**
717 |
718 | ```bash
719 | # 1. Create PR (github-release-manager)
720 | gh pr create --title "feat: new feature" --body "..."
721 |
722 | # 2. Start watch mode with GraphQL tracking
723 | bash scripts/pr/watch_reviews.sh 212 180 &
724 |
725 | # 3. When review arrives, auto-review handles everything:
726 | bash scripts/pr/auto_review.sh 212 5 true
727 | # - Fetches review feedback
728 | # - Categorizes issues
729 | # - Generates fixes
730 | # - Applies and commits
731 | # - Pushes to PR branch
732 | # - **Auto-resolves threads** ← NEW!
733 | # - Triggers new review
734 | # - Repeats until approved
735 |
736 | # 4. Merge when approved (github-release-manager)
737 | gh pr merge 212 --squash
738 | ```
739 |
740 | **Documentation:**
741 |
742 | See `docs/pr-graphql-integration.md` for:
743 | - Complete API reference
744 | - Troubleshooting guide
745 | - GraphQL query examples
746 | - Advanced usage patterns
747 | - Performance considerations
748 |
749 | ## Integration with github-release-manager
750 |
751 | This agent **extends** the github-release-manager workflow:
752 |
753 | **github-release-manager handles**:
754 | - Version bumping
755 | - CHANGELOG/README updates
756 | - PR creation
757 | - Issue tracking
758 | - Post-release actions
759 |
760 | **gemini-pr-automator adds**:
761 | - Automated review iteration
762 | - Fix application
763 | - Test generation
764 | - Quality gates
765 | - Breaking change detection
766 |
767 | **Combined Workflow**:
768 | 1. `github-release-manager` creates release PR
769 | 2. `gemini-pr-automator` runs quality gate
770 | 3. `gemini-pr-automator` triggers automated review loop
771 | 4. `github-release-manager` merges when approved
772 | 5. `github-release-manager` handles post-release tasks
773 |
774 | ## Project-Specific Patterns
775 |
776 | ### MCP Memory Service PR Standards
777 |
778 | **Required Checks**:
779 | - ✅ All tests pass (`pytest tests/`)
780 | - ✅ No security vulnerabilities
781 | - ✅ Code complexity ≤7 for new functions
782 | - ✅ Type hints on all new functions
783 | - ✅ Breaking changes documented in CHANGELOG
784 |
785 | **Review Focus Areas**:
786 | - Storage backend modifications (critical path)
787 | - MCP tool schema changes (protocol compliance)
788 | - Web API endpoints (security implications)
789 | - Hook system changes (user-facing)
790 | - Performance-critical code (5ms target)
791 |
792 | ### Gemini Review Iteration Pattern
793 |
794 | **Iteration 1**: Initial review (broad feedback)
795 | **Iteration 2**: Apply safe fixes, re-review specific areas
796 | **Iteration 3**: Address remaining issues, focus on edge cases
797 | **Iteration 4**: Final polish, documentation review
798 | **Iteration 5**: Approval or escalate to manual review
799 |
800 | ## Usage Examples
801 |
802 | ### Quick Automated Review
803 |
804 | ```bash
805 | # Standard automated review (5 iterations, safe fixes enabled)
806 | bash scripts/pr/auto_review.sh 123
807 |
808 | # Conservative mode (3 iterations, manual fixes)
809 | bash scripts/pr/auto_review.sh 123 3 false
810 |
811 | # Aggressive mode (10 iterations, auto-fix everything)
812 | bash scripts/pr/auto_review.sh 123 10 true
813 | ```
814 |
815 | ### Generate Tests Only
816 |
817 | ```bash
818 | # Generate tests for PR #123
819 | bash scripts/pr/generate_tests.sh 123
820 |
821 | # Review generated tests
822 | ls -la /tmp/test_gen_*.py
823 | ```
824 |
825 | ### Breaking Change Check
826 |
827 | ```bash
828 | # Check if PR introduces breaking changes
829 | bash scripts/pr/detect_breaking_changes.sh main feature/new-api
830 |
831 | # Exit code 0 = no breaking changes
832 | # Exit code 1 = breaking changes detected
833 | ```
834 |
835 | ## Best Practices
836 |
837 | 1. **Always run quality gate first**: Catch issues before review iteration
838 | 2. **Start with safe-fix mode off**: Observe behavior before trusting automation
839 | 3. **Review auto-applied commits**: Ensure changes make sense before merging
840 | 4. **Limit iterations**: Prevent infinite loops, escalate to humans at max
841 | 5. **Document breaking changes**: Always update CHANGELOG for API changes
842 | 6. **Test generated tests**: Verify generated tests actually work before committing
843 |
844 | ## Limitations
845 |
846 | - **Context limitations**: Gemini has context limits, very large PRs may need manual review
847 | - **Fix quality**: Auto-generated fixes may not always be optimal (human review recommended)
848 | - **False negatives**: Breaking change detection may miss subtle breaking changes
849 | - **API rate limits**: Gemini CLI subject to rate limits, add delays between iterations
850 | - **Complexity**: Multi-file refactoring with complex dependencies needs manual oversight
851 |
852 | ## Performance Considerations
853 |
854 | - Single review iteration: ~90-120 seconds (Gemini response time)
855 | - Full automated cycle (5 iterations): ~7-10 minutes
856 | - Test generation per file: ~30-60 seconds
857 | - Breaking change detection: ~15-30 seconds
858 |
859 | **Time Savings**: ~10-30 minutes saved per PR vs manual iteration
860 |
861 | ---
862 |
863 | **Quick Reference Card**:
864 |
865 | ```bash
866 | # Full automated review
867 | bash scripts/pr/full_auto_review.sh <PR_NUMBER>
868 |
869 | # Quality gate only
870 | bash scripts/pr/quality_gate.sh <PR_NUMBER>
871 |
872 | # Generate tests
873 | bash scripts/pr/generate_tests.sh <PR_NUMBER>
874 |
875 | # Breaking changes
876 | bash scripts/pr/detect_breaking_changes.sh main <BRANCH>
877 |
878 | # Auto-review with options
879 | bash scripts/pr/auto_review.sh <PR_NUMBER> <MAX_ITER> <SAFE_FIX:true/false>
880 | ```
881 |
```
--------------------------------------------------------------------------------
/scripts/migration/migrate_v5_enhanced.py:
--------------------------------------------------------------------------------
```python
1 | #!/usr/bin/env python3
2 | # Copyright 2024 Heinrich Krupp
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | """
17 | Enhanced ChromaDB to SQLite-vec Migration Script for v5.0.0+
18 |
19 | This script provides a robust migration path from ChromaDB to SQLite-vec with:
20 | - Custom data path support
21 | - Proper content hash generation
22 | - Tag format validation and correction
23 | - Progress indicators
24 | - Transaction-based migration with rollback
25 | - Dry-run mode for testing
26 | - Comprehensive error handling
27 | """
28 |
29 | import argparse
30 | import asyncio
31 | import hashlib
32 | import json
33 | import logging
34 | import os
35 | import sqlite3
36 | import sys
37 | import tempfile
38 | import time
39 | from datetime import datetime
40 | from pathlib import Path
41 | from typing import List, Dict, Any, Optional, Union, Tuple
42 |
43 | # Try importing with progress bar support
44 | try:
45 | from tqdm import tqdm
46 | TQDM_AVAILABLE = True
47 | except ImportError:
48 | TQDM_AVAILABLE = False
49 | print("Note: Install 'tqdm' for progress bars: pip install tqdm")
50 |
51 | # Add project root to path
52 | project_root = Path(__file__).parent.parent
53 | sys.path.insert(0, str(project_root / "src"))
54 |
55 | # Import storage modules
56 | try:
57 | from mcp_memory_service.storage.chroma import ChromaMemoryStorage
58 | from mcp_memory_service.storage.sqlite_vec import SqliteVecMemoryStorage
59 | from mcp_memory_service.models.memory import Memory
60 | from mcp_memory_service.utils.hashing import generate_content_hash
61 | except ImportError as e:
62 | print(f"Error importing MCP modules: {e}")
63 | print("Make sure you're running this from the MCP Memory Service directory")
64 | sys.exit(1)
65 |
66 | # Setup logging
67 | logging.basicConfig(
68 | level=logging.INFO,
69 | format='%(asctime)s - %(levelname)s - %(message)s'
70 | )
71 | logger = logging.getLogger(__name__)
72 |
73 |
74 | class MigrationConfig:
75 | """Configuration for migration process."""
76 |
77 | def __init__(self):
78 | self.chroma_path: Optional[str] = None
79 | self.sqlite_path: Optional[str] = None
80 | self.batch_size: int = 50
81 | self.dry_run: bool = False
82 | self.skip_duplicates: bool = True
83 | self.backup_path: Optional[str] = None
84 | self.verbose: bool = False
85 | self.validate_only: bool = False
86 | self.force: bool = False
87 |
88 | @classmethod
89 | def from_args(cls, args) -> 'MigrationConfig':
90 | """Create config from command line arguments."""
91 | config = cls()
92 | config.chroma_path = args.chroma_path
93 | config.sqlite_path = args.sqlite_path
94 | config.batch_size = args.batch_size
95 | config.dry_run = args.dry_run
96 | config.skip_duplicates = not args.no_skip_duplicates
97 | config.backup_path = args.backup
98 | config.verbose = args.verbose
99 | config.validate_only = args.validate_only
100 | config.force = args.force
101 | return config
102 |
103 | def resolve_paths(self):
104 | """Resolve and validate data paths."""
105 | # Resolve ChromaDB path
106 | if not self.chroma_path:
107 | # Check environment variable first
108 | self.chroma_path = os.environ.get('MCP_MEMORY_CHROMA_PATH')
109 |
110 | if not self.chroma_path:
111 | # Use default locations based on platform
112 | home = Path.home()
113 | if sys.platform == 'darwin': # macOS
114 | default_base = home / 'Library' / 'Application Support' / 'mcp-memory'
115 | elif sys.platform == 'win32': # Windows
116 | default_base = Path(os.getenv('LOCALAPPDATA', '')) / 'mcp-memory'
117 | else: # Linux
118 | default_base = home / '.local' / 'share' / 'mcp-memory'
119 |
120 | # Try multiple possible locations
121 | possible_paths = [
122 | home / '.mcp_memory_chroma', # Legacy location
123 | default_base / 'chroma_db', # New standard location
124 | Path.cwd() / 'chroma_db', # Current directory
125 | ]
126 |
127 | for path in possible_paths:
128 | if path.exists():
129 | self.chroma_path = str(path)
130 | logger.info(f"Found ChromaDB at: {path}")
131 | break
132 |
133 | if not self.chroma_path:
134 | raise ValueError(
135 | "Could not find ChromaDB data. Please specify --chroma-path or "
136 | "set MCP_MEMORY_CHROMA_PATH environment variable"
137 | )
138 |
139 | # Resolve SQLite path
140 | if not self.sqlite_path:
141 | # Check environment variable first
142 | self.sqlite_path = os.environ.get('MCP_MEMORY_SQLITE_PATH')
143 |
144 | if not self.sqlite_path:
145 | # Default to same directory as ChromaDB with different name
146 | chroma_dir = Path(self.chroma_path).parent
147 | self.sqlite_path = str(chroma_dir / 'sqlite_vec.db')
148 | logger.info(f"Using default SQLite path: {self.sqlite_path}")
149 |
150 | # Resolve backup path if needed
151 | if self.backup_path is None and not self.dry_run and not self.validate_only:
152 | backup_dir = Path(self.sqlite_path).parent / 'backups'
153 | backup_dir.mkdir(exist_ok=True)
154 | timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
155 | self.backup_path = str(backup_dir / f'migration_backup_{timestamp}.json')
156 |
157 |
158 | class EnhancedMigrationTool:
159 | """Enhanced migration tool with proper error handling and progress tracking."""
160 |
161 | def __init__(self, config: MigrationConfig):
162 | self.config = config
163 | self.stats = {
164 | 'total': 0,
165 | 'migrated': 0,
166 | 'skipped': 0,
167 | 'failed': 0,
168 | 'errors': []
169 | }
170 | self.chroma_storage = None
171 | self.sqlite_storage = None
172 |
173 | def generate_proper_content_hash(self, content: str) -> str:
174 | """Generate a proper SHA256 content hash."""
175 | return hashlib.sha256(content.encode()).hexdigest()
176 |
177 | def validate_and_fix_tags(self, tags: Any) -> List[str]:
178 | """Validate and fix tag format."""
179 | if not tags:
180 | return []
181 |
182 | if isinstance(tags, str):
183 | # Handle comma-separated string
184 | if ',' in tags:
185 | return [tag.strip() for tag in tags.split(',') if tag.strip()]
186 | # Handle single tag
187 | return [tags.strip()] if tags.strip() else []
188 |
189 | if isinstance(tags, list):
190 | # Clean and validate list tags
191 | clean_tags = []
192 | for tag in tags:
193 | if isinstance(tag, str) and tag.strip():
194 | clean_tags.append(tag.strip())
195 | return clean_tags
196 |
197 | # Unknown format - log warning and return empty
198 | logger.warning(f"Unknown tag format: {type(tags)} - {tags}")
199 | return []
200 |
201 | def safe_timestamp_convert(self, timestamp: Any) -> float:
202 | """Safely convert various timestamp formats to float."""
203 | if timestamp is None:
204 | return datetime.now().timestamp()
205 |
206 | # Handle numeric timestamps
207 | if isinstance(timestamp, (int, float)):
208 | # Check if timestamp is reasonable (between 2000 and 2100)
209 | if 946684800 <= float(timestamp) <= 4102444800:
210 | return float(timestamp)
211 | else:
212 | logger.warning(f"Timestamp {timestamp} out of reasonable range, using current time")
213 | return datetime.now().timestamp()
214 |
215 | # Handle string timestamps
216 | if isinstance(timestamp, str):
217 | # Try ISO format
218 | for fmt in [
219 | '%Y-%m-%dT%H:%M:%S.%f',
220 | '%Y-%m-%dT%H:%M:%S',
221 | '%Y-%m-%d %H:%M:%S.%f',
222 | '%Y-%m-%d %H:%M:%S',
223 | ]:
224 | try:
225 | dt = datetime.strptime(timestamp.rstrip('Z'), fmt)
226 | return dt.timestamp()
227 | except ValueError:
228 | continue
229 |
230 | # Try parsing as float string
231 | try:
232 | ts = float(timestamp)
233 | if 946684800 <= ts <= 4102444800:
234 | return ts
235 | except ValueError:
236 | pass
237 |
238 | # Fallback to current time
239 | logger.warning(f"Could not parse timestamp '{timestamp}', using current time")
240 | return datetime.now().timestamp()
241 |
242 | async def extract_memories_from_chroma(self) -> List[Dict[str, Any]]:
243 | """Extract all memories from ChromaDB with proper error handling."""
244 | memories = []
245 |
246 | try:
247 | # Initialize ChromaDB storage
248 | logger.info("Connecting to ChromaDB...")
249 | self.chroma_storage = ChromaMemoryStorage(path=self.config.chroma_path)
250 |
251 | # Access the collection directly
252 | collection = self.chroma_storage.collection
253 | if not collection:
254 | raise ValueError("ChromaDB collection not initialized")
255 |
256 | # Get all data from collection
257 | logger.info("Extracting memories from ChromaDB...")
258 | results = collection.get(include=['documents', 'metadatas', 'embeddings'])
259 |
260 | if not results or not results.get('ids'):
261 | logger.warning("No memories found in ChromaDB")
262 | return memories
263 |
264 | total = len(results['ids'])
265 | logger.info(f"Found {total} memories to process")
266 |
267 | # Process each memory
268 | for i in range(total):
269 | try:
270 | # Extract data with defaults
271 | doc_id = results['ids'][i]
272 | content = results['documents'][i] if i < len(results.get('documents', [])) else ""
273 | metadata = results['metadatas'][i] if i < len(results.get('metadatas', [])) else {}
274 | embedding = results['embeddings'][i] if i < len(results.get('embeddings', [])) else None
275 |
276 | if not content:
277 | logger.warning(f"Skipping memory {doc_id}: empty content")
278 | continue
279 |
280 | # Generate proper content hash
281 | content_hash = self.generate_proper_content_hash(content)
282 |
283 | # Extract and validate tags
284 | raw_tags = metadata.get('tags', metadata.get('tags_str', []))
285 | tags = self.validate_and_fix_tags(raw_tags)
286 |
287 | # Extract timestamps
288 | created_at = self.safe_timestamp_convert(
289 | metadata.get('created_at', metadata.get('timestamp'))
290 | )
291 | updated_at = self.safe_timestamp_convert(
292 | metadata.get('updated_at', created_at)
293 | )
294 |
295 | # Extract memory type
296 | memory_type = metadata.get('memory_type', metadata.get('type', 'imported'))
297 |
298 | # Clean metadata (remove special fields)
299 | clean_metadata = {}
300 | exclude_keys = {
301 | 'tags', 'tags_str', 'created_at', 'updated_at',
302 | 'timestamp', 'timestamp_float', 'timestamp_str',
303 | 'memory_type', 'type', 'content_hash',
304 | 'created_at_iso', 'updated_at_iso'
305 | }
306 |
307 | for key, value in metadata.items():
308 | if key not in exclude_keys and value is not None:
309 | clean_metadata[key] = value
310 |
311 | # Create memory record
312 | memory_data = {
313 | 'content': content,
314 | 'content_hash': content_hash,
315 | 'tags': tags,
316 | 'memory_type': memory_type,
317 | 'metadata': clean_metadata,
318 | 'embedding': embedding,
319 | 'created_at': created_at,
320 | 'updated_at': updated_at,
321 | 'original_id': doc_id # Keep for reference
322 | }
323 |
324 | memories.append(memory_data)
325 |
326 | if self.config.verbose and (i + 1) % 100 == 0:
327 | logger.info(f"Processed {i + 1}/{total} memories")
328 |
329 | except Exception as e:
330 | logger.error(f"Failed to extract memory {i}: {e}")
331 | self.stats['errors'].append(f"Extract error at index {i}: {str(e)}")
332 | continue
333 |
334 | logger.info(f"Successfully extracted {len(memories)} memories")
335 | return memories
336 |
337 | except Exception as e:
338 | logger.error(f"Critical error extracting from ChromaDB: {e}")
339 | raise
340 |
341 | async def migrate_to_sqlite(self, memories: List[Dict[str, Any]]) -> bool:
342 | """Migrate memories to SQLite-vec with transaction support."""
343 | if not memories:
344 | logger.warning("No memories to migrate")
345 | return True
346 |
347 | try:
348 | # Initialize SQLite-vec storage
349 | logger.info(f"Initializing SQLite-vec at {self.config.sqlite_path}")
350 | self.sqlite_storage = SqliteVecMemoryStorage(
351 | db_path=self.config.sqlite_path,
352 | embedding_model=os.environ.get('MCP_MEMORY_EMBEDDING_MODEL', 'all-MiniLM-L6-v2')
353 | )
354 | await self.sqlite_storage.initialize()
355 |
356 | # Start transaction
357 | conn = self.sqlite_storage.conn
358 | conn.execute("BEGIN TRANSACTION")
359 |
360 | try:
361 | # Migrate memories in batches
362 | total = len(memories)
363 | batch_size = self.config.batch_size
364 |
365 | # Use progress bar if available
366 | if TQDM_AVAILABLE and not self.config.dry_run:
367 | progress_bar = tqdm(total=total, desc="Migrating memories")
368 | else:
369 | progress_bar = None
370 |
371 | for i in range(0, total, batch_size):
372 | batch = memories[i:i + batch_size]
373 |
374 | if not self.config.dry_run:
375 | for memory_data in batch:
376 | try:
377 | # Check for duplicates
378 | if self.config.skip_duplicates:
379 | existing = conn.execute(
380 | "SELECT 1 FROM memories WHERE content_hash = ? LIMIT 1",
381 | (memory_data['content_hash'],)
382 | ).fetchone()
383 |
384 | if existing:
385 | self.stats['skipped'] += 1
386 | if progress_bar:
387 | progress_bar.update(1)
388 | continue
389 |
390 | # Create Memory object
391 | memory = Memory(
392 | content=memory_data['content'],
393 | content_hash=memory_data['content_hash'],
394 | tags=memory_data['tags'],
395 | memory_type=memory_data.get('memory_type'),
396 | metadata=memory_data.get('metadata', {}),
397 | created_at=memory_data['created_at'],
398 | updated_at=memory_data['updated_at']
399 | )
400 |
401 | # Store memory
402 | success, message = await self.sqlite_storage.store(memory)
403 |
404 | if success:
405 | self.stats['migrated'] += 1
406 | else:
407 | raise Exception(f"Failed to store: {message}")
408 |
409 | if progress_bar:
410 | progress_bar.update(1)
411 |
412 | except Exception as e:
413 | self.stats['failed'] += 1
414 | self.stats['errors'].append(
415 | f"Migration error for {memory_data['content_hash'][:8]}: {str(e)}"
416 | )
417 | if progress_bar:
418 | progress_bar.update(1)
419 |
420 | else:
421 | # Dry run - just count
422 | self.stats['migrated'] += len(batch)
423 | if progress_bar:
424 | progress_bar.update(len(batch))
425 |
426 | if progress_bar:
427 | progress_bar.close()
428 |
429 | # Commit transaction
430 | if not self.config.dry_run:
431 | conn.execute("COMMIT")
432 | logger.info("Transaction committed successfully")
433 | else:
434 | conn.execute("ROLLBACK")
435 | logger.info("Dry run - transaction rolled back")
436 |
437 | return True
438 |
439 | except Exception as e:
440 | # Rollback on error
441 | conn.execute("ROLLBACK")
442 | logger.error(f"Migration failed, transaction rolled back: {e}")
443 | raise
444 |
445 | except Exception as e:
446 | logger.error(f"Critical error during migration: {e}")
447 | return False
448 |
449 | finally:
450 | # Clean up
451 | if self.sqlite_storage:
452 | self.sqlite_storage.close()
453 |
454 | async def create_backup(self, memories: List[Dict[str, Any]]):
455 | """Create a JSON backup of memories."""
456 | if not self.config.backup_path or self.config.dry_run:
457 | return
458 |
459 | logger.info(f"Creating backup at {self.config.backup_path}")
460 |
461 | backup_data = {
462 | 'version': '2.0',
463 | 'created_at': datetime.now().isoformat(),
464 | 'source': self.config.chroma_path,
465 | 'total_memories': len(memories),
466 | 'memories': memories
467 | }
468 |
469 | # Remove embeddings from backup to reduce size
470 | for memory in backup_data['memories']:
471 | memory.pop('embedding', None)
472 |
473 | with open(self.config.backup_path, 'w') as f:
474 | json.dump(backup_data, f, indent=2, default=str)
475 |
476 | logger.info(f"Backup created: {self.config.backup_path}")
477 |
478 | async def validate_migration(self) -> bool:
479 | """Validate the migrated data."""
480 | logger.info("Validating migration...")
481 |
482 | try:
483 | # Connect to SQLite database
484 | conn = sqlite3.connect(self.config.sqlite_path)
485 |
486 | # Check memory count
487 | count = conn.execute("SELECT COUNT(*) FROM memories").fetchone()[0]
488 | logger.info(f"SQLite database contains {count} memories")
489 |
490 | # Check for required fields
491 | sample = conn.execute("""
492 | SELECT content_hash, content, tags, created_at
493 | FROM memories
494 | LIMIT 10
495 | """).fetchall()
496 |
497 | issues = []
498 | for row in sample:
499 | if not row[0]: # content_hash
500 | issues.append("Missing content_hash")
501 | if not row[1]: # content
502 | issues.append("Missing content")
503 | if row[3] is None: # created_at
504 | issues.append("Missing created_at")
505 |
506 | conn.close()
507 |
508 | if issues:
509 | logger.warning(f"Validation issues found: {', '.join(set(issues))}")
510 | return False
511 |
512 | logger.info("Validation passed!")
513 | return True
514 |
515 | except Exception as e:
516 | logger.error(f"Validation failed: {e}")
517 | return False
518 |
519 | async def run(self) -> bool:
520 | """Run the migration process."""
521 | try:
522 | # Resolve paths
523 | self.config.resolve_paths()
524 |
525 | # Print configuration
526 | print("\n" + "="*60)
527 | print("MCP Memory Service - Enhanced Migration Tool v2.0")
528 | print("="*60)
529 | print(f"ChromaDB source: {self.config.chroma_path}")
530 | print(f"SQLite-vec target: {self.config.sqlite_path}")
531 | if self.config.backup_path:
532 | print(f"Backup location: {self.config.backup_path}")
533 | print(f"Mode: {'DRY RUN' if self.config.dry_run else 'LIVE MIGRATION'}")
534 | print(f"Batch size: {self.config.batch_size}")
535 | print(f"Skip duplicates: {self.config.skip_duplicates}")
536 | print()
537 |
538 | # Check if validation only
539 | if self.config.validate_only:
540 | return await self.validate_migration()
541 |
542 | # Check if target exists
543 | if Path(self.config.sqlite_path).exists() and not self.config.force:
544 | if not self.config.dry_run:
545 | response = input(f"Target database exists. Overwrite? (y/N): ")
546 | if response.lower() != 'y':
547 | print("Migration cancelled")
548 | return False
549 |
550 | # Extract memories from ChromaDB
551 | memories = await self.extract_memories_from_chroma()
552 | self.stats['total'] = len(memories)
553 |
554 | if not memories:
555 | print("No memories found to migrate")
556 | return True
557 |
558 | # Create backup
559 | if self.config.backup_path and not self.config.dry_run:
560 | await self.create_backup(memories)
561 |
562 | # Confirm migration
563 | if not self.config.dry_run and not self.config.force:
564 | print(f"\nAbout to migrate {len(memories)} memories")
565 | response = input("Proceed? (y/N): ")
566 | if response.lower() != 'y':
567 | print("Migration cancelled")
568 | return False
569 |
570 | # Perform migration
571 | success = await self.migrate_to_sqlite(memories)
572 |
573 | # Print summary
574 | print("\n" + "="*60)
575 | print("MIGRATION SUMMARY")
576 | print("="*60)
577 | print(f"Total memories found: {self.stats['total']}")
578 | print(f"Successfully migrated: {self.stats['migrated']}")
579 | print(f"Duplicates skipped: {self.stats['skipped']}")
580 | print(f"Failed migrations: {self.stats['failed']}")
581 |
582 | if self.stats['errors'] and self.config.verbose:
583 | print("\nErrors encountered:")
584 | for i, error in enumerate(self.stats['errors'][:10], 1):
585 | print(f" {i}. {error}")
586 | if len(self.stats['errors']) > 10:
587 | print(f" ... and {len(self.stats['errors']) - 10} more")
588 |
589 | if success and not self.config.dry_run:
590 | # Validate migration
591 | if await self.validate_migration():
592 | print("\n✅ Migration completed successfully!")
593 | print("\nNext steps:")
594 | print("1. Set environment variable:")
595 | print(" export MCP_MEMORY_STORAGE_BACKEND=sqlite_vec")
596 | print(f"2. Set database path:")
597 | print(f" export MCP_MEMORY_SQLITE_PATH={self.config.sqlite_path}")
598 | print("3. Restart MCP Memory Service")
599 | print("4. Test that your memories are accessible")
600 | else:
601 | print("\n⚠️ Migration completed with validation warnings")
602 |
603 | return success
604 |
605 | except Exception as e:
606 | logger.error(f"Migration failed: {e}")
607 | if self.config.verbose:
608 | import traceback
609 | traceback.print_exc()
610 | return False
611 |
612 |
613 | def main():
614 | """Main entry point."""
615 | parser = argparse.ArgumentParser(
616 | description="Enhanced ChromaDB to SQLite-vec migration tool",
617 | formatter_class=argparse.RawDescriptionHelpFormatter
618 | )
619 |
620 | parser.add_argument(
621 | '--chroma-path',
622 | help='Path to ChromaDB data directory (default: auto-detect)'
623 | )
624 | parser.add_argument(
625 | '--sqlite-path',
626 | help='Path for SQLite-vec database (default: same dir as ChromaDB)'
627 | )
628 | parser.add_argument(
629 | '--batch-size',
630 | type=int,
631 | default=50,
632 | help='Number of memories to migrate per batch (default: 50)'
633 | )
634 | parser.add_argument(
635 | '--dry-run',
636 | action='store_true',
637 | help='Simulate migration without making changes'
638 | )
639 | parser.add_argument(
640 | '--no-skip-duplicates',
641 | action='store_true',
642 | help='Migrate all memories including duplicates'
643 | )
644 | parser.add_argument(
645 | '--backup',
646 | help='Path for JSON backup file (default: auto-generate)'
647 | )
648 | parser.add_argument(
649 | '--verbose',
650 | action='store_true',
651 | help='Enable verbose logging'
652 | )
653 | parser.add_argument(
654 | '--validate-only',
655 | action='store_true',
656 | help='Only validate existing SQLite database'
657 | )
658 | parser.add_argument(
659 | '--force',
660 | action='store_true',
661 | help='Skip confirmation prompts'
662 | )
663 |
664 | args = parser.parse_args()
665 |
666 | # Set logging level
667 | if args.verbose:
668 | logging.getLogger().setLevel(logging.DEBUG)
669 |
670 | # Create configuration
671 | config = MigrationConfig.from_args(args)
672 |
673 | # Run migration
674 | tool = EnhancedMigrationTool(config)
675 | success = asyncio.run(tool.run())
676 |
677 | sys.exit(0 if success else 1)
678 |
679 |
680 | if __name__ == "__main__":
681 | main()
```
--------------------------------------------------------------------------------
/docs/examples/tag-schema.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "tag_schema": {
3 | "version": "2.0",
4 | "last_updated": "2025-06-07",
5 | "description": "Standardized tag schema for MCP Memory Service knowledge management",
6 | "total_categories": 6,
7 | "naming_convention": {
8 | "format": "lowercase-with-hyphens",
9 | "rules": [
10 | "Use lowercase letters only",
11 | "Replace spaces with hyphens",
12 | "Use descriptive but concise terms",
13 | "Avoid abbreviations unless widely understood",
14 | "Use singular form when possible"
15 | ],
16 | "examples": {
17 | "good": ["memory-service", "github-integration", "best-practices"],
18 | "bad": ["memoryservice", "GitHub_Integration", "bestPractices"]
19 | }
20 | },
21 | "categories": {
22 | "projects_and_repositories": {
23 | "description": "Primary projects, repositories, and major components",
24 | "color": "#3b82f6",
25 | "icon": "🚀",
26 | "tags": {
27 | "primary_projects": [
28 | {
29 | "tag": "mcp-memory-service",
30 | "description": "Core memory service development",
31 | "usage_count": 45,
32 | "examples": ["Core development", "Memory storage features", "Database operations"]
33 | },
34 | {
35 | "tag": "memory-dashboard",
36 | "description": "Dashboard application for memory management",
37 | "usage_count": 23,
38 | "examples": ["UI development", "Dashboard features", "User interface"]
39 | },
40 | {
41 | "tag": "github-integration",
42 | "description": "GitHub connectivity and automation",
43 | "usage_count": 15,
44 | "examples": ["Issue tracking", "Repository management", "CI/CD"]
45 | },
46 | {
47 | "tag": "mcp-protocol",
48 | "description": "Model Context Protocol development",
49 | "usage_count": 12,
50 | "examples": ["Protocol specifications", "Communication standards"]
51 | },
52 | {
53 | "tag": "cloudflare-workers",
54 | "description": "Edge computing integration",
55 | "usage_count": 8,
56 | "examples": ["Edge deployment", "Worker scripts", "Distributed systems"]
57 | }
58 | ],
59 | "project_components": [
60 | {
61 | "tag": "frontend",
62 | "description": "User interface components and client-side development",
63 | "usage_count": 18,
64 | "examples": ["React components", "UI/UX", "Client applications"]
65 | },
66 | {
67 | "tag": "backend",
68 | "description": "Server-side development and APIs",
69 | "usage_count": 32,
70 | "examples": ["Server logic", "Database operations", "API development"]
71 | },
72 | {
73 | "tag": "api",
74 | "description": "API design and implementation",
75 | "usage_count": 14,
76 | "examples": ["REST APIs", "Endpoints", "API documentation"]
77 | },
78 | {
79 | "tag": "database",
80 | "description": "Data storage and management",
81 | "usage_count": 22,
82 | "examples": ["ChromaDB", "Data models", "Database optimization"]
83 | },
84 | {
85 | "tag": "infrastructure",
86 | "description": "Deployment and DevOps",
87 | "usage_count": 16,
88 | "examples": ["Docker", "Cloud deployment", "System architecture"]
89 | }
90 | ]
91 | }
92 | },
93 | "technologies_and_tools": {
94 | "description": "Programming languages, frameworks, libraries, and development tools",
95 | "color": "#10b981",
96 | "icon": "⚙️",
97 | "tags": {
98 | "programming_languages": [
99 | {
100 | "tag": "python",
101 | "description": "Python development and scripts",
102 | "usage_count": 38,
103 | "examples": ["Backend development", "Scripts", "Data processing"]
104 | },
105 | {
106 | "tag": "typescript",
107 | "description": "TypeScript development",
108 | "usage_count": 25,
109 | "examples": ["Frontend development", "Type safety", "React applications"]
110 | },
111 | {
112 | "tag": "javascript",
113 | "description": "JavaScript development",
114 | "usage_count": 20,
115 | "examples": ["Client-side logic", "Node.js", "Web development"]
116 | },
117 | {
118 | "tag": "bash",
119 | "description": "Shell scripting and command-line operations",
120 | "usage_count": 12,
121 | "examples": ["Automation scripts", "System administration", "Build processes"]
122 | },
123 | {
124 | "tag": "sql",
125 | "description": "Database queries and operations",
126 | "usage_count": 8,
127 | "examples": ["Database queries", "Data analysis", "Schema design"]
128 | }
129 | ],
130 | "frameworks_and_libraries": [
131 | {
132 | "tag": "react",
133 | "description": "React framework development",
134 | "usage_count": 22,
135 | "examples": ["Component development", "UI frameworks", "Frontend applications"]
136 | },
137 | {
138 | "tag": "fastapi",
139 | "description": "FastAPI framework for Python APIs",
140 | "usage_count": 15,
141 | "examples": ["API development", "Web services", "Backend frameworks"]
142 | },
143 | {
144 | "tag": "chromadb",
145 | "description": "ChromaDB vector database",
146 | "usage_count": 28,
147 | "examples": ["Vector storage", "Embedding operations", "Similarity search"]
148 | },
149 | {
150 | "tag": "sentence-transformers",
151 | "description": "Sentence transformer models for embeddings",
152 | "usage_count": 18,
153 | "examples": ["Text embeddings", "Semantic search", "NLP models"]
154 | },
155 | {
156 | "tag": "pytest",
157 | "description": "Python testing framework",
158 | "usage_count": 10,
159 | "examples": ["Unit testing", "Test automation", "Quality assurance"]
160 | }
161 | ],
162 | "tools_and_platforms": [
163 | {
164 | "tag": "git",
165 | "description": "Version control and repository management",
166 | "usage_count": 24,
167 | "examples": ["Version control", "Collaboration", "Code management"]
168 | },
169 | {
170 | "tag": "docker",
171 | "description": "Containerization and deployment",
172 | "usage_count": 16,
173 | "examples": ["Container deployment", "Application packaging", "DevOps"]
174 | },
175 | {
176 | "tag": "github",
177 | "description": "GitHub platform and repository management",
178 | "usage_count": 20,
179 | "examples": ["Repository hosting", "Issue tracking", "Collaboration"]
180 | },
181 | {
182 | "tag": "aws",
183 | "description": "Amazon Web Services cloud platform",
184 | "usage_count": 12,
185 | "examples": ["Cloud infrastructure", "Deployment", "Scalability"]
186 | },
187 | {
188 | "tag": "npm",
189 | "description": "Node package management",
190 | "usage_count": 8,
191 | "examples": ["Package management", "Dependencies", "JavaScript ecosystem"]
192 | }
193 | ]
194 | }
195 | },
196 | "activities_and_processes": {
197 | "description": "Development activities, operational processes, and workflows",
198 | "color": "#f59e0b",
199 | "icon": "🔧",
200 | "tags": {
201 | "development_activities": [
202 | {
203 | "tag": "development",
204 | "description": "General development work and programming",
205 | "usage_count": 35,
206 | "examples": ["Feature development", "Code writing", "Implementation"]
207 | },
208 | {
209 | "tag": "implementation",
210 | "description": "Feature implementation and code realization",
211 | "usage_count": 28,
212 | "examples": ["Feature implementation", "Code realization", "System building"]
213 | },
214 | {
215 | "tag": "debugging",
216 | "description": "Bug investigation and problem solving",
217 | "usage_count": 22,
218 | "examples": ["Bug fixes", "Problem investigation", "Issue resolution"]
219 | },
220 | {
221 | "tag": "testing",
222 | "description": "Quality assurance and testing activities",
223 | "usage_count": 30,
224 | "examples": ["Unit testing", "Integration testing", "Quality assurance"]
225 | },
226 | {
227 | "tag": "refactoring",
228 | "description": "Code improvement and restructuring",
229 | "usage_count": 12,
230 | "examples": ["Code cleanup", "Architecture improvement", "Optimization"]
231 | },
232 | {
233 | "tag": "optimization",
234 | "description": "Performance enhancement and efficiency improvements",
235 | "usage_count": 15,
236 | "examples": ["Performance tuning", "Resource optimization", "Speed improvements"]
237 | }
238 | ],
239 | "documentation_activities": [
240 | {
241 | "tag": "documentation",
242 | "description": "Writing documentation and guides",
243 | "usage_count": 25,
244 | "examples": ["Technical documentation", "User guides", "API documentation"]
245 | },
246 | {
247 | "tag": "tutorial",
248 | "description": "Creating tutorials and learning materials",
249 | "usage_count": 8,
250 | "examples": ["Step-by-step guides", "Learning materials", "How-to documents"]
251 | },
252 | {
253 | "tag": "guide",
254 | "description": "Comprehensive guides and references",
255 | "usage_count": 12,
256 | "examples": ["Best practice guides", "Implementation guides", "Reference materials"]
257 | },
258 | {
259 | "tag": "reference",
260 | "description": "Reference materials and quick lookups",
261 | "usage_count": 18,
262 | "examples": ["Quick reference", "Lookup tables", "Technical specifications"]
263 | },
264 | {
265 | "tag": "examples",
266 | "description": "Code examples and practical demonstrations",
267 | "usage_count": 15,
268 | "examples": ["Code samples", "Usage examples", "Demonstrations"]
269 | }
270 | ],
271 | "operational_activities": [
272 | {
273 | "tag": "deployment",
274 | "description": "Application deployment and release management",
275 | "usage_count": 18,
276 | "examples": ["Production deployment", "Release management", "Environment setup"]
277 | },
278 | {
279 | "tag": "monitoring",
280 | "description": "System monitoring and observability",
281 | "usage_count": 10,
282 | "examples": ["Performance monitoring", "Health checks", "System observability"]
283 | },
284 | {
285 | "tag": "backup",
286 | "description": "Data backup and recovery processes",
287 | "usage_count": 8,
288 | "examples": ["Data backup", "Disaster recovery", "Data preservation"]
289 | },
290 | {
291 | "tag": "migration",
292 | "description": "Data or system migration processes",
293 | "usage_count": 12,
294 | "examples": ["Data migration", "System upgrades", "Platform transitions"]
295 | },
296 | {
297 | "tag": "maintenance",
298 | "description": "System maintenance and upkeep",
299 | "usage_count": 15,
300 | "examples": ["Regular maintenance", "System updates", "Preventive care"]
301 | },
302 | {
303 | "tag": "troubleshooting",
304 | "description": "Problem resolution and diagnostic work",
305 | "usage_count": 20,
306 | "examples": ["Issue diagnosis", "Problem solving", "System repair"]
307 | }
308 | ]
309 | }
310 | },
311 | "content_types_and_formats": {
312 | "description": "Types of knowledge content and documentation formats",
313 | "color": "#8b5cf6",
314 | "icon": "📚",
315 | "tags": {
316 | "knowledge_types": [
317 | {
318 | "tag": "concept",
319 | "description": "Conceptual information and theoretical content",
320 | "usage_count": 18,
321 | "examples": ["Design concepts", "Theoretical frameworks", "Ideas"]
322 | },
323 | {
324 | "tag": "architecture",
325 | "description": "System architecture and design patterns",
326 | "usage_count": 22,
327 | "examples": ["System design", "Architecture patterns", "Technical blueprints"]
328 | },
329 | {
330 | "tag": "design",
331 | "description": "Design decisions and design patterns",
332 | "usage_count": 16,
333 | "examples": ["Design decisions", "UI/UX design", "System design"]
334 | },
335 | {
336 | "tag": "best-practices",
337 | "description": "Proven methodologies and recommended approaches",
338 | "usage_count": 20,
339 | "examples": ["Industry standards", "Recommended practices", "Quality guidelines"]
340 | },
341 | {
342 | "tag": "methodology",
343 | "description": "Systematic approaches and methodologies",
344 | "usage_count": 12,
345 | "examples": ["Development methodologies", "Process frameworks", "Systematic approaches"]
346 | },
347 | {
348 | "tag": "workflow",
349 | "description": "Process workflows and operational procedures",
350 | "usage_count": 14,
351 | "examples": ["Business processes", "Development workflows", "Operational procedures"]
352 | }
353 | ],
354 | "documentation_formats": [
355 | {
356 | "tag": "tutorial",
357 | "description": "Step-by-step instructional content",
358 | "usage_count": 15,
359 | "examples": ["Learning tutorials", "How-to guides", "Educational content"]
360 | },
361 | {
362 | "tag": "reference",
363 | "description": "Quick reference materials and lookups",
364 | "usage_count": 18,
365 | "examples": ["API reference", "Command reference", "Quick lookups"]
366 | },
367 | {
368 | "tag": "example",
369 | "description": "Practical examples and demonstrations",
370 | "usage_count": 22,
371 | "examples": ["Code examples", "Use cases", "Practical demonstrations"]
372 | },
373 | {
374 | "tag": "template",
375 | "description": "Reusable templates and boilerplates",
376 | "usage_count": 10,
377 | "examples": ["Document templates", "Code templates", "Process templates"]
378 | },
379 | {
380 | "tag": "checklist",
381 | "description": "Verification checklists and task lists",
382 | "usage_count": 8,
383 | "examples": ["Quality checklists", "Process verification", "Task lists"]
384 | },
385 | {
386 | "tag": "summary",
387 | "description": "Condensed information and overviews",
388 | "usage_count": 12,
389 | "examples": ["Executive summaries", "Project overviews", "Condensed reports"]
390 | }
391 | ],
392 | "technical_content": [
393 | {
394 | "tag": "configuration",
395 | "description": "System configuration and setup information",
396 | "usage_count": 16,
397 | "examples": ["System setup", "Configuration files", "Environment setup"]
398 | },
399 | {
400 | "tag": "specification",
401 | "description": "Technical specifications and requirements",
402 | "usage_count": 14,
403 | "examples": ["Technical specs", "Requirements", "Standards"]
404 | },
405 | {
406 | "tag": "analysis",
407 | "description": "Technical analysis and research findings",
408 | "usage_count": 18,
409 | "examples": ["Performance analysis", "Technical research", "Data analysis"]
410 | },
411 | {
412 | "tag": "research",
413 | "description": "Research findings and investigations",
414 | "usage_count": 15,
415 | "examples": ["Research results", "Investigations", "Study findings"]
416 | },
417 | {
418 | "tag": "review",
419 | "description": "Code reviews and process evaluations",
420 | "usage_count": 10,
421 | "examples": ["Code reviews", "Process reviews", "Quality assessments"]
422 | }
423 | ]
424 | }
425 | },
426 | "status_and_progress": {
427 | "description": "Development status, progress indicators, and priority levels",
428 | "color": "#ef4444",
429 | "icon": "📊",
430 | "tags": {
431 | "development_status": [
432 | {
433 | "tag": "resolved",
434 | "description": "Completed and verified work",
435 | "usage_count": 25,
436 | "examples": ["Completed features", "Fixed bugs", "Resolved issues"]
437 | },
438 | {
439 | "tag": "in-progress",
440 | "description": "Currently being worked on",
441 | "usage_count": 18,
442 | "examples": ["Active development", "Ongoing work", "Current tasks"]
443 | },
444 | {
445 | "tag": "blocked",
446 | "description": "Waiting for external dependencies",
447 | "usage_count": 8,
448 | "examples": ["Dependency blocks", "External waiting", "Resource constraints"]
449 | },
450 | {
451 | "tag": "needs-investigation",
452 | "description": "Requires further analysis or research",
453 | "usage_count": 12,
454 | "examples": ["Research needed", "Analysis required", "Investigation pending"]
455 | },
456 | {
457 | "tag": "planned",
458 | "description": "Scheduled for future work",
459 | "usage_count": 15,
460 | "examples": ["Future work", "Roadmap items", "Planned features"]
461 | },
462 | {
463 | "tag": "cancelled",
464 | "description": "No longer being pursued",
465 | "usage_count": 5,
466 | "examples": ["Cancelled projects", "Deprecated features", "Abandoned work"]
467 | }
468 | ],
469 | "quality_status": [
470 | {
471 | "tag": "verified",
472 | "description": "Tested and confirmed working",
473 | "usage_count": 20,
474 | "examples": ["Verified functionality", "Confirmed working", "Quality assured"]
475 | },
476 | {
477 | "tag": "tested",
478 | "description": "Has undergone testing",
479 | "usage_count": 22,
480 | "examples": ["Tested code", "QA complete", "Testing done"]
481 | },
482 | {
483 | "tag": "reviewed",
484 | "description": "Has been peer reviewed",
485 | "usage_count": 15,
486 | "examples": ["Code reviewed", "Peer reviewed", "Quality checked"]
487 | },
488 | {
489 | "tag": "approved",
490 | "description": "Officially approved for use",
491 | "usage_count": 12,
492 | "examples": ["Management approved", "Officially sanctioned", "Authorized"]
493 | },
494 | {
495 | "tag": "experimental",
496 | "description": "Proof of concept or experimental stage",
497 | "usage_count": 8,
498 | "examples": ["Proof of concept", "Experimental features", "Research stage"]
499 | },
500 | {
501 | "tag": "deprecated",
502 | "description": "No longer recommended for use",
503 | "usage_count": 6,
504 | "examples": ["Legacy code", "Outdated practices", "Superseded methods"]
505 | }
506 | ],
507 | "priority_levels": [
508 | {
509 | "tag": "urgent",
510 | "description": "Immediate attention required",
511 | "usage_count": 8,
512 | "examples": ["Critical bugs", "Emergency fixes", "Immediate action"]
513 | },
514 | {
515 | "tag": "high-priority",
516 | "description": "Important, should be addressed soon",
517 | "usage_count": 15,
518 | "examples": ["Important features", "Key improvements", "High-impact work"]
519 | },
520 | {
521 | "tag": "normal-priority",
522 | "description": "Standard priority work",
523 | "usage_count": 25,
524 | "examples": ["Regular work", "Standard features", "Normal development"]
525 | },
526 | {
527 | "tag": "low-priority",
528 | "description": "Can be addressed when time allows",
529 | "usage_count": 18,
530 | "examples": ["Nice-to-have features", "Minor improvements", "Low-impact work"]
531 | },
532 | {
533 | "tag": "nice-to-have",
534 | "description": "Enhancement, not critical",
535 | "usage_count": 12,
536 | "examples": ["Optional features", "Enhancements", "Convenience improvements"]
537 | }
538 | ]
539 | }
540 | },
541 | "context_and_temporal": {
542 | "description": "Temporal markers, environmental context, and scope indicators",
543 | "color": "#06b6d4",
544 | "icon": "🕒",
545 | "tags": {
546 | "temporal_markers": [
547 | {
548 | "tag": "january-2025",
549 | "description": "Content from January 2025",
550 | "usage_count": 50,
551 | "examples": ["Project initialization", "Early development", "Planning phase"]
552 | },
553 | {
554 | "tag": "june-2025",
555 | "description": "Content from June 2025",
556 | "usage_count": 45,
557 | "examples": ["Recent development", "Current work", "Latest updates"]
558 | },
559 | {
560 | "tag": "q1-2025",
561 | "description": "First quarter 2025 content",
562 | "usage_count": 18,
563 | "examples": ["Quarterly planning", "Q1 objectives", "First quarter work"]
564 | },
565 | {
566 | "tag": "milestone-v1",
567 | "description": "Version 1 milestone content",
568 | "usage_count": 12,
569 | "examples": ["Version milestones", "Release markers", "Development phases"]
570 | },
571 | {
572 | "tag": "sprint-3",
573 | "description": "Development sprint markers",
574 | "usage_count": 8,
575 | "examples": ["Sprint work", "Iteration markers", "Development cycles"]
576 | }
577 | ],
578 | "environmental_context": [
579 | {
580 | "tag": "development",
581 | "description": "Development environment context",
582 | "usage_count": 30,
583 | "examples": ["Development work", "Local environment", "Dev testing"]
584 | },
585 | {
586 | "tag": "staging",
587 | "description": "Staging environment context",
588 | "usage_count": 12,
589 | "examples": ["Staging deployment", "Pre-production", "Staging testing"]
590 | },
591 | {
592 | "tag": "production",
593 | "description": "Production environment context",
594 | "usage_count": 20,
595 | "examples": ["Live systems", "Production deployment", "Production issues"]
596 | },
597 | {
598 | "tag": "testing",
599 | "description": "Testing environment context",
600 | "usage_count": 25,
601 | "examples": ["Test environment", "QA testing", "Testing infrastructure"]
602 | },
603 | {
604 | "tag": "local",
605 | "description": "Local development context",
606 | "usage_count": 15,
607 | "examples": ["Local development", "Local testing", "Local setup"]
608 | }
609 | ],
610 | "scope_and_impact": [
611 | {
612 | "tag": "breaking-change",
613 | "description": "Introduces breaking changes",
614 | "usage_count": 8,
615 | "examples": ["API changes", "Backwards incompatible", "Major updates"]
616 | },
617 | {
618 | "tag": "feature",
619 | "description": "New feature development",
620 | "usage_count": 28,
621 | "examples": ["New features", "Feature additions", "Functionality expansion"]
622 | },
623 | {
624 | "tag": "enhancement",
625 | "description": "Improvement to existing features",
626 | "usage_count": 22,
627 | "examples": ["Feature improvements", "Performance enhancements", "User experience"]
628 | },
629 | {
630 | "tag": "hotfix",
631 | "description": "Critical fix for production issues",
632 | "usage_count": 6,
633 | "examples": ["Emergency fixes", "Critical patches", "Production fixes"]
634 | },
635 | {
636 | "tag": "security",
637 | "description": "Security-related content",
638 | "usage_count": 10,
639 | "examples": ["Security fixes", "Security analysis", "Vulnerability patches"]
640 | },
641 | {
642 | "tag": "performance",
643 | "description": "Performance-related improvements",
644 | "usage_count": 15,
645 | "examples": ["Performance optimization", "Speed improvements", "Efficiency gains"]
646 | }
647 | ]
648 | }
649 | }
650 | },
651 | "tag_combination_patterns": {
652 | "description": "Common patterns for combining tags across categories",
653 | "examples": [
654 | {
655 | "pattern": "Project + Technology + Activity + Status",
656 | "example": ["mcp-memory-service", "python", "debugging", "resolved"],
657 | "usage": "Most comprehensive tagging for technical work"
658 | },
659 | {
660 | "pattern": "Content Type + Domain + Technology + Context",
661 | "example": ["documentation", "backend", "chromadb", "production"],
662 | "usage": "Documentation and reference materials"
663 | },
664 | {
665 | "pattern": "Activity + Status + Priority + Temporal",
666 | "example": ["testing", "in-progress", "high-priority", "june-2025"],
667 | "usage": "Active work items with clear status"
668 | },
669 | {
670 | "pattern": "Concept + Architecture + Research + Domain",
671 | "example": ["concept", "architecture", "research", "system-design"],
672 | "usage": "Conceptual and design-related content"
673 | }
674 | ]
675 | },
676 | "usage_guidelines": {
677 | "recommended_tag_count": {
678 | "minimum": 3,
679 | "maximum": 8,
680 | "optimal": "4-6 tags from different categories"
681 | },
682 | "category_distribution": {
683 | "required": ["Project context (1-2 tags)", "Content type or activity (1-2 tags)"],
684 | "recommended": ["Technology (1-2 tags)", "Status (1 tag)"],
685 | "optional": ["Context/Temporal (0-2 tags)", "Priority (0-1 tags)"]
686 | },
687 | "quality_indicators": [
688 | "Tags from multiple categories",
689 | "Specific rather than generic terms",
690 | "Consistent with established patterns",
691 | "Relevant to content and future retrieval"
692 | ]
693 | },
694 | "maintenance": {
695 | "review_schedule": {
696 | "weekly": "Check new tag usage patterns",
697 | "monthly": "Review tag frequency and consistency",
698 | "quarterly": "Update schema based on usage patterns"
699 | },
700 | "evolution_process": [
701 | "Identify new patterns in content",
702 | "Propose new tags or categories",
703 | "Test with sample content",
704 | "Update schema documentation",
705 | "Migrate existing content if needed"
706 | ]
707 | }
708 | }
709 | }
```
--------------------------------------------------------------------------------
/claude-hooks/tests/integration-test.js:
--------------------------------------------------------------------------------
```javascript
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Integration Test for Claude Code Memory Awareness Hooks
5 | * Tests the complete Phase 1 implementation end-to-end
6 | */
7 |
8 | const fs = require('fs');
9 | const fsPromises = require('fs').promises;
10 | const path = require('path');
11 | const os = require('os');
12 |
13 | // Import hooks and utilities
14 | const sessionStartHook = require('../core/session-start');
15 | const sessionEndHook = require('../core/session-end');
16 | const { detectProjectContext } = require('../utilities/project-detector');
17 | const { scoreMemoryRelevance } = require('../utilities/memory-scorer');
18 | const { formatMemoriesForContext } = require('../utilities/context-formatter');
19 |
20 | /**
21 | * Test Results Tracker
22 | */
23 | class TestResults {
24 | constructor() {
25 | this.tests = [];
26 | this.passed = 0;
27 | this.failed = 0;
28 | }
29 |
30 | test(name, testFn) {
31 | console.log(`\n🧪 Testing: ${name}`);
32 | try {
33 | const result = testFn();
34 | if (result === true || (result && result.success !== false)) {
35 | console.log(`✅ PASS: ${name}`);
36 | this.passed++;
37 | this.tests.push({ name, status: 'PASS', result });
38 | } else {
39 | console.log(`❌ FAIL: ${name} - ${result.error || 'Test returned false'}`);
40 | this.failed++;
41 | this.tests.push({ name, status: 'FAIL', error: result.error || 'Test returned false' });
42 | }
43 | } catch (error) {
44 | console.log(`❌ FAIL: ${name} - ${error.message}`);
45 | this.failed++;
46 | this.tests.push({ name, status: 'FAIL', error: error.message });
47 | }
48 | }
49 |
50 | async asyncTest(name, testFn) {
51 | console.log(`\n🧪 Testing: ${name}`);
52 | try {
53 | const result = await testFn();
54 | if (result === true || (result && result.success !== false)) {
55 | console.log(`✅ PASS: ${name}`);
56 | this.passed++;
57 | this.tests.push({ name, status: 'PASS', result });
58 | } else {
59 | console.log(`❌ FAIL: ${name} - ${result.error || 'Test returned false'}`);
60 | this.failed++;
61 | this.tests.push({ name, status: 'FAIL', error: result.error || 'Test returned false' });
62 | }
63 | } catch (error) {
64 | console.log(`❌ FAIL: ${name} - ${error.message}`);
65 | this.failed++;
66 | this.tests.push({ name, status: 'FAIL', error: error.message });
67 | }
68 | }
69 |
70 | summary() {
71 | console.log('\n' + '='.repeat(60));
72 | console.log('🎯 TEST SUMMARY');
73 | console.log('='.repeat(60));
74 | console.log(`Total Tests: ${this.tests.length}`);
75 | console.log(`✅ Passed: ${this.passed}`);
76 | console.log(`❌ Failed: ${this.failed}`);
77 | console.log(`Success Rate: ${((this.passed / this.tests.length) * 100).toFixed(1)}%`);
78 |
79 | if (this.failed > 0) {
80 | console.log('\n🔍 FAILED TESTS:');
81 | this.tests.filter(t => t.status === 'FAIL').forEach(test => {
82 | console.log(` - ${test.name}: ${test.error}`);
83 | });
84 | }
85 |
86 | console.log('='.repeat(60));
87 | return this.failed === 0;
88 | }
89 | }
90 |
91 | /**
92 | * Mock data for testing
93 | */
94 | const mockMemories = [
95 | {
96 | content: 'Decided to use SQLite-vec instead of ChromaDB for better performance in MCP Memory Service. SQLite-vec provides 10x faster startup and uses 75% less memory.',
97 | tags: ['mcp-memory-service', 'decision', 'sqlite-vec', 'performance'],
98 | memory_type: 'decision',
99 | created_at_iso: '2025-08-19T10:00:00Z'
100 | },
101 | {
102 | content: 'Implemented comprehensive Claude Code hooks system for automatic memory awareness. Created session-start, session-end, and topic-change hooks with project detection.',
103 | tags: ['claude-code', 'hooks', 'architecture', 'memory-awareness'],
104 | memory_type: 'architecture',
105 | created_at_iso: '2025-08-19T09:30:00Z'
106 | },
107 | {
108 | content: 'Fixed critical bug in project detector - was not handling pyproject.toml files correctly. Added proper Python project detection.',
109 | tags: ['bug-fix', 'project-detector', 'python'],
110 | memory_type: 'bug-fix',
111 | created_at_iso: '2025-08-18T15:30:00Z'
112 | },
113 | {
114 | content: 'Learning session on memory relevance scoring algorithms. Implemented time decay, tag matching, and content analysis for intelligent memory selection.',
115 | tags: ['learning', 'algorithms', 'memory-scoring'],
116 | memory_type: 'insight',
117 | created_at_iso: '2025-08-17T14:00:00Z'
118 | },
119 | {
120 | content: 'Random note about completely unrelated project for testing filtering',
121 | tags: ['other-project', 'unrelated', 'test'],
122 | memory_type: 'note',
123 | created_at_iso: '2025-08-01T08:00:00Z'
124 | }
125 | ];
126 |
127 | const mockProjectContext = {
128 | name: 'mcp-memory-service',
129 | directory: process.cwd(),
130 | language: 'JavaScript',
131 | frameworks: ['Node.js'],
132 | tools: ['npm'],
133 | git: {
134 | isRepo: true,
135 | branch: 'main',
136 | repoName: 'mcp-memory-service',
137 | lastCommit: 'abc1234 Implement memory awareness hooks'
138 | },
139 | confidence: 0.9
140 | };
141 |
142 | const mockConversation = {
143 | messages: [
144 | {
145 | role: 'user',
146 | content: 'I need to implement a memory awareness system for Claude Code that automatically injects relevant project memories.'
147 | },
148 | {
149 | role: 'assistant',
150 | content: 'I\'ll help you create a comprehensive memory awareness system. We decided to use Claude Code hooks for session management and implement automatic context injection. This will include project detection, memory scoring, and intelligent context formatting.'
151 | },
152 | {
153 | role: 'user',
154 | content: 'Great! I learned that we need project detection algorithms and memory scoring systems. Can you implement the project detector?'
155 | },
156 | {
157 | role: 'assistant',
158 | content: 'Exactly. I implemented the project detector in project-detector.js with support for multiple languages and frameworks. I also created memory scoring algorithms with time decay and relevance matching. Next we need to test the complete system and add session consolidation.'
159 | }
160 | ]
161 | };
162 |
163 | /**
164 | * Run comprehensive tests
165 | */
166 | async function runTests() {
167 | console.log('🚀 Claude Code Memory Awareness - Integration Tests');
168 | console.log('Testing Phase 1 Implementation\n');
169 |
170 | const results = new TestResults();
171 |
172 | // Test 1: Project Detection
173 | await results.asyncTest('Project Detection', async () => {
174 | const context = await detectProjectContext(process.cwd());
175 |
176 | if (!context.name) {
177 | return { success: false, error: 'No project name detected' };
178 | }
179 |
180 | if (!context.language) {
181 | return { success: false, error: 'No language detected' };
182 | }
183 |
184 | console.log(` Detected: ${context.name} (${context.language}), Confidence: ${(context.confidence * 100).toFixed(1)}%`);
185 | return { success: true, context };
186 | });
187 |
188 | // Test 2: Memory Relevance Scoring
189 | results.test('Memory Relevance Scoring', () => {
190 | const scored = scoreMemoryRelevance(mockMemories, mockProjectContext);
191 |
192 | if (!Array.isArray(scored)) {
193 | return { success: false, error: 'Scoring did not return array' };
194 | }
195 |
196 | if (scored.length !== mockMemories.length) {
197 | return { success: false, error: 'Scoring lost memories' };
198 | }
199 |
200 | // Check that memories have scores
201 | for (const memory of scored) {
202 | if (typeof memory.relevanceScore !== 'number') {
203 | return { success: false, error: 'Memory missing relevance score' };
204 | }
205 | }
206 |
207 | // Check that memories are sorted by relevance (highest first)
208 | for (let i = 1; i < scored.length; i++) {
209 | if (scored[i].relevanceScore > scored[i-1].relevanceScore) {
210 | return { success: false, error: 'Memories not sorted by relevance' };
211 | }
212 | }
213 |
214 | console.log(` Scored ${scored.length} memories, top score: ${scored[0].relevanceScore.toFixed(3)}`);
215 | return { success: true, scored };
216 | });
217 |
218 | // Test 3: Context Formatting
219 | results.test('Context Formatting', () => {
220 | const scored = scoreMemoryRelevance(mockMemories, mockProjectContext);
221 | const formatted = formatMemoriesForContext(scored, mockProjectContext);
222 |
223 | if (typeof formatted !== 'string') {
224 | return { success: false, error: 'Formatting did not return string' };
225 | }
226 |
227 | if (formatted.length < 100) {
228 | return { success: false, error: 'Formatted context too short' };
229 | }
230 |
231 | // Check for key formatting elements
232 | if (!formatted.includes('Memory Context')) {
233 | return { success: false, error: 'Missing memory context header' };
234 | }
235 |
236 | if (!formatted.includes(mockProjectContext.name)) {
237 | return { success: false, error: 'Missing project name in context' };
238 | }
239 |
240 | console.log(` Generated ${formatted.length} characters of formatted context`);
241 | return { success: true, formatted };
242 | });
243 |
244 | // Test 4: Session Start Hook Structure
245 | results.test('Session Start Hook Structure', () => {
246 | if (typeof sessionStartHook.handler !== 'function') {
247 | return { success: false, error: 'Session start hook missing handler function' };
248 | }
249 |
250 | if (!sessionStartHook.name || !sessionStartHook.version) {
251 | return { success: false, error: 'Session start hook missing metadata' };
252 | }
253 |
254 | if (sessionStartHook.trigger !== 'session-start') {
255 | return { success: false, error: 'Session start hook wrong trigger' };
256 | }
257 |
258 | console.log(` Hook: ${sessionStartHook.name} v${sessionStartHook.version}`);
259 | return { success: true };
260 | });
261 |
262 | // Test 5: Session End Hook Structure
263 | results.test('Session End Hook Structure', () => {
264 | if (typeof sessionEndHook.handler !== 'function') {
265 | return { success: false, error: 'Session end hook missing handler function' };
266 | }
267 |
268 | if (!sessionEndHook.name || !sessionEndHook.version) {
269 | return { success: false, error: 'Session end hook missing metadata' };
270 | }
271 |
272 | if (sessionEndHook.trigger !== 'session-end') {
273 | return { success: false, error: 'Session end hook wrong trigger' };
274 | }
275 |
276 | console.log(` Hook: ${sessionEndHook.name} v${sessionEndHook.version}`);
277 | return { success: true };
278 | });
279 |
280 | // Test 5a: parseTranscript - String content messages
281 | await results.asyncTest('parseTranscript: String content messages', async () => {
282 | const { parseTranscript } = sessionEndHook._internal;
283 | if (!parseTranscript) {
284 | return { success: false, error: 'parseTranscript not exported' };
285 | }
286 |
287 | const entries = [
288 | { type: 'user', message: { role: 'user', content: 'Hello, how are you?' } },
289 | { type: 'assistant', message: { role: 'assistant', content: 'I am doing well!' } }
290 | ];
291 | const tmpFile = path.join(os.tmpdir(), `test-transcript-${Date.now()}.jsonl`);
292 | await fsPromises.writeFile(tmpFile, entries.map(e => JSON.stringify(e)).join('\n'), 'utf8');
293 |
294 | try {
295 | const result = await parseTranscript(tmpFile);
296 | if (result.messages.length !== 2) {
297 | return { success: false, error: `Expected 2 messages, got ${result.messages.length}` };
298 | }
299 | if (result.messages[0].content !== 'Hello, how are you?') {
300 | return { success: false, error: `Unexpected content: ${result.messages[0].content}` };
301 | }
302 | console.log(` Parsed ${result.messages.length} messages from JSONL`);
303 | return { success: true };
304 | } finally {
305 | await fsPromises.unlink(tmpFile).catch(() => {});
306 | }
307 | });
308 |
309 | // Test 5b: parseTranscript - Array content blocks
310 | await results.asyncTest('parseTranscript: Array content blocks', async () => {
311 | const { parseTranscript } = sessionEndHook._internal;
312 | const entries = [
313 | {
314 | type: 'assistant',
315 | message: {
316 | role: 'assistant',
317 | content: [
318 | { type: 'text', text: 'First part.' },
319 | { type: 'text', text: 'Second part.' }
320 | ]
321 | }
322 | }
323 | ];
324 | const tmpFile = path.join(os.tmpdir(), `test-transcript-${Date.now()}.jsonl`);
325 | await fsPromises.writeFile(tmpFile, entries.map(e => JSON.stringify(e)).join('\n'), 'utf8');
326 |
327 | try {
328 | const result = await parseTranscript(tmpFile);
329 | if (result.messages.length !== 1) {
330 | return { success: false, error: `Expected 1 message, got ${result.messages.length}` };
331 | }
332 | if (!result.messages[0].content.includes('First part')) {
333 | return { success: false, error: 'Missing first part in content' };
334 | }
335 | console.log(` Correctly joined array content blocks`);
336 | return { success: true };
337 | } finally {
338 | await fsPromises.unlink(tmpFile).catch(() => {});
339 | }
340 | });
341 |
342 | // Test 5c: parseTranscript - Skips non-user/assistant entries
343 | await results.asyncTest('parseTranscript: Skips non-message entries', async () => {
344 | const { parseTranscript } = sessionEndHook._internal;
345 | const entries = [
346 | { type: 'file-history-snapshot', message: null },
347 | { type: 'user', message: { role: 'user', content: 'Hello' } },
348 | { type: 'system', message: { role: 'system', content: 'System msg' } }
349 | ];
350 | const tmpFile = path.join(os.tmpdir(), `test-transcript-${Date.now()}.jsonl`);
351 | await fsPromises.writeFile(tmpFile, entries.map(e => JSON.stringify(e)).join('\n'), 'utf8');
352 |
353 | try {
354 | const result = await parseTranscript(tmpFile);
355 | if (result.messages.length !== 1) {
356 | return { success: false, error: `Expected 1 message (user only), got ${result.messages.length}` };
357 | }
358 | console.log(` Correctly filtered to user/assistant messages only`);
359 | return { success: true };
360 | } finally {
361 | await fsPromises.unlink(tmpFile).catch(() => {});
362 | }
363 | });
364 |
365 | // Test 5d: parseTranscript - Handles malformed JSON gracefully
366 | await results.asyncTest('parseTranscript: Handles malformed JSON', async () => {
367 | const { parseTranscript } = sessionEndHook._internal;
368 | const tmpFile = path.join(os.tmpdir(), `test-transcript-${Date.now()}.jsonl`);
369 | const content = [
370 | JSON.stringify({ type: 'user', message: { role: 'user', content: 'Valid' } }),
371 | 'not valid json {{{',
372 | JSON.stringify({ type: 'assistant', message: { role: 'assistant', content: 'Also valid' } })
373 | ].join('\n');
374 | await fsPromises.writeFile(tmpFile, content, 'utf8');
375 |
376 | try {
377 | const result = await parseTranscript(tmpFile);
378 | if (result.messages.length !== 2) {
379 | return { success: false, error: `Expected 2 messages (skipping malformed), got ${result.messages.length}` };
380 | }
381 | console.log(` Gracefully skipped malformed JSON line`);
382 | return { success: true };
383 | } finally {
384 | await fsPromises.unlink(tmpFile).catch(() => {});
385 | }
386 | });
387 |
388 | // Test 6: Configuration Loading
389 | results.test('Configuration Loading', () => {
390 | const configPath = path.join(__dirname, '../config.json');
391 |
392 | if (!fs.existsSync(configPath)) {
393 | return { success: false, error: 'Configuration file not found' };
394 | }
395 |
396 | try {
397 | const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
398 |
399 | if (!config.memoryService) {
400 | return { success: false, error: 'Invalid configuration structure' };
401 | }
402 |
403 | // Support both old (direct endpoint) and new (dual-protocol) structures
404 | const endpoint = config.memoryService.endpoint || config.memoryService.http?.endpoint;
405 |
406 | if (!endpoint) {
407 | return { success: false, error: 'No endpoint configured (checked both old and new format)' };
408 | }
409 |
410 | console.log(` Endpoint: ${endpoint}`);
411 | return { success: true, config };
412 |
413 | } catch (error) {
414 | return { success: false, error: `Configuration parse error: ${error.message}` };
415 | }
416 | });
417 |
418 | // Test 7: File Structure
419 | results.test('File Structure Validation', () => {
420 | const requiredFiles = [
421 | '../core/session-start.js',
422 | '../core/session-end.js',
423 | '../utilities/project-detector.js',
424 | '../utilities/memory-scorer.js',
425 | '../utilities/context-formatter.js',
426 | '../config.json',
427 | '../config.template.json',
428 | '../README.md'
429 | ];
430 |
431 | for (const file of requiredFiles) {
432 | const fullPath = path.join(__dirname, file);
433 | if (!fs.existsSync(fullPath)) {
434 | return { success: false, error: `Missing required file: ${file}` };
435 | }
436 | }
437 |
438 | console.log(` All ${requiredFiles.length} required files present`);
439 | return { success: true };
440 | });
441 |
442 | // Test 8: Mock Session Start (Limited Test)
443 | await results.asyncTest('Mock Session Start Hook', async () => {
444 | const mockContext = {
445 | workingDirectory: process.cwd(),
446 | sessionId: 'test-session',
447 | injectSystemMessage: async (message) => {
448 | if (typeof message !== 'string' || message.length < 50) {
449 | throw new Error('Invalid message injection');
450 | }
451 | console.log(` Injected ${message.length} characters of context`);
452 | return true;
453 | }
454 | };
455 |
456 | try {
457 | // Note: This will attempt to contact the memory service
458 | // In a real test environment, we'd mock this
459 | await sessionStartHook.handler(mockContext);
460 | return { success: true };
461 | } catch (error) {
462 | // Expected to fail without real memory service connection or when dependencies are missing
463 | if (error.message.includes('Network error') ||
464 | error.message.includes('ENOTFOUND') ||
465 | error.message.includes('memoryClient is not defined') ||
466 | error.message.includes('No active connection')) {
467 | console.log(' ⚠️ Expected error (no memory service or connection available)');
468 | console.log(' This is expected if the service is not running during tests');
469 | return { success: true }; // This is expected in test environment
470 | }
471 | throw error;
472 | }
473 | });
474 |
475 | // Test 9: Package Dependencies
476 | results.test('Package Dependencies Check', () => {
477 | const requiredModules = ['fs', 'path', 'https', 'child_process'];
478 |
479 | for (const module of requiredModules) {
480 | try {
481 | require(module);
482 | } catch (error) {
483 | return { success: false, error: `Missing required module: ${module}` };
484 | }
485 | }
486 |
487 | console.log(` All ${requiredModules.length} required Node.js modules available`);
488 | return { success: true };
489 | });
490 |
491 | // Test 10: Claude Code Settings Validation
492 | results.test('Claude Code Settings Configuration', () => {
493 | const settingsPath = path.join(process.env.HOME, '.claude', 'settings.json');
494 |
495 | if (!fs.existsSync(settingsPath)) {
496 | return { success: false, error: 'Claude Code settings.json not found' };
497 | }
498 |
499 | try {
500 | const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
501 |
502 | // Check for hooks configuration
503 | if (!settings.hooks) {
504 | return { success: false, error: 'No hooks configuration found in settings' };
505 | }
506 |
507 | // Check for SessionStart hook
508 | if (!settings.hooks.SessionStart || !Array.isArray(settings.hooks.SessionStart)) {
509 | return { success: false, error: 'SessionStart hooks not configured' };
510 | }
511 |
512 | // Check for SessionEnd hook
513 | if (!settings.hooks.SessionEnd || !Array.isArray(settings.hooks.SessionEnd)) {
514 | return { success: false, error: 'SessionEnd hooks not configured' };
515 | }
516 |
517 | // Check hook command paths
518 | const startHook = JSON.stringify(settings.hooks.SessionStart);
519 | const endHook = JSON.stringify(settings.hooks.SessionEnd);
520 |
521 | if (!startHook.includes('session-start.js')) {
522 | return { success: false, error: 'SessionStart hook command not configured correctly' };
523 | }
524 |
525 | if (!endHook.includes('session-end.js')) {
526 | return { success: false, error: 'SessionEnd hook command not configured correctly' };
527 | }
528 |
529 | console.log(' Claude Code settings configured correctly');
530 | return { success: true, settings };
531 |
532 | } catch (parseError) {
533 | return { success: false, error: `Settings parse error: ${parseError.message}` };
534 | }
535 | });
536 |
537 | // Test 11: Hook Files Location Validation
538 | results.test('Hook Files in Correct Location', () => {
539 | const hookDir = path.join(process.env.HOME, '.claude', 'hooks');
540 | const requiredHooks = [
541 | 'core/session-start.js',
542 | 'core/session-end.js',
543 | 'utilities/project-detector.js',
544 | 'utilities/memory-scorer.js',
545 | 'utilities/context-formatter.js'
546 | ];
547 |
548 | for (const hookFile of requiredHooks) {
549 | const fullPath = path.join(hookDir, hookFile);
550 | if (!fs.existsSync(fullPath)) {
551 | return { success: false, error: `Hook file missing: ${hookFile}` };
552 | }
553 | }
554 |
555 | console.log(` All hooks installed in ${hookDir}`);
556 | return { success: true };
557 | });
558 |
559 | // Test 12: Claude Code CLI Availability
560 | results.test('Claude Code CLI Availability', () => {
561 | const { execSync } = require('child_process');
562 |
563 | try {
564 | execSync('which claude', { stdio: 'pipe' });
565 | console.log(' Claude Code CLI available');
566 | return { success: true };
567 | } catch (error) {
568 | return { success: false, error: 'Claude Code CLI not found in PATH' };
569 | }
570 | });
571 |
572 | // Test 13: Memory Service Protocol
573 | results.test('Memory Service Protocol Compatibility', () => {
574 | // Test that we're generating the correct MCP JSON-RPC calls
575 | const testCall = {
576 | jsonrpc: '2.0',
577 | id: 1,
578 | method: 'tools/call',
579 | params: {
580 | name: 'retrieve_memory',
581 | arguments: {
582 | query: 'test query',
583 | tags: ['test'],
584 | limit: 5
585 | }
586 | }
587 | };
588 |
589 | const serialized = JSON.stringify(testCall);
590 | const parsed = JSON.parse(serialized);
591 |
592 | if (!parsed.jsonrpc || parsed.jsonrpc !== '2.0') {
593 | return { success: false, error: 'Invalid JSON-RPC format' };
594 | }
595 |
596 | if (!parsed.params || !parsed.params.name || !parsed.params.arguments) {
597 | return { success: false, error: 'Invalid MCP call structure' };
598 | }
599 |
600 | console.log(` MCP protocol structure valid`);
601 | return { success: true };
602 | });
603 |
604 | // Test 14: Memory Service Connectivity
605 | await results.asyncTest('Memory Service Connectivity', async () => {
606 | const configPath = path.join(__dirname, '../config.json');
607 |
608 | if (!fs.existsSync(configPath)) {
609 | return { success: false, error: 'Configuration file not found for connectivity test' };
610 | }
611 |
612 | try {
613 | const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
614 |
615 | // Support both old (direct) and new (dual-protocol) structures
616 | const endpoint = config.memoryService?.endpoint || config.memoryService?.http?.endpoint;
617 | const apiKey = config.memoryService?.apiKey || config.memoryService?.http?.apiKey;
618 |
619 | if (!endpoint) {
620 | return { success: false, error: 'No memory service endpoint configured (checked both old and new format)' };
621 | }
622 |
623 | // Test basic connectivity (simplified test)
624 | const https = require('https');
625 | const url = new URL('/api/health', endpoint);
626 |
627 | return new Promise((resolve) => {
628 | const options = {
629 | hostname: url.hostname,
630 | port: url.port || 8443,
631 | path: url.pathname,
632 | method: 'GET',
633 | timeout: 5000,
634 | rejectUnauthorized: false
635 | };
636 |
637 | const req = https.request(options, (res) => {
638 | console.log(` Memory service responded with status: ${res.statusCode}`);
639 | if (res.statusCode === 200 || res.statusCode === 401) {
640 | // 401 is expected without API key, but service is running
641 | resolve({ success: true });
642 | } else {
643 | resolve({ success: false, error: `Service returned status: ${res.statusCode}` });
644 | }
645 | });
646 |
647 | req.on('error', (error) => {
648 | // Mark as success with warning if service isn't running (expected in test environments)
649 | console.log(` ⚠️ Memory service not available: ${error.message}`);
650 | console.log(' This is expected if the service is not running during tests');
651 | resolve({ success: true });
652 | });
653 |
654 | req.on('timeout', () => {
655 | console.log(' ⚠️ Connection timeout - service may not be running');
656 | console.log(' This is expected if the service is not running during tests');
657 | resolve({ success: true });
658 | });
659 |
660 | req.end();
661 | });
662 |
663 | } catch (parseError) {
664 | return { success: false, error: `Configuration parse error: ${parseError.message}` };
665 | }
666 | });
667 |
668 | // Display summary
669 | const allTestsPassed = results.summary();
670 |
671 | if (allTestsPassed) {
672 | console.log('\n🎉 ALL TESTS PASSED! Phase 1 implementation is ready.');
673 | console.log('\n📋 Next Steps:');
674 | console.log(' 1. Install hooks in Claude Code hooks directory');
675 | console.log(' 2. Configure memory service endpoint in config.json');
676 | console.log(' 3. Test with real Claude Code session');
677 | console.log(' 4. Begin Phase 2 implementation (dynamic memory loading)');
678 | } else {
679 | console.log('\n⚠️ Some tests failed. Please fix issues before proceeding.');
680 | }
681 |
682 | return allTestsPassed;
683 | }
684 |
685 | // Run tests if called directly
686 | if (require.main === module) {
687 | runTests()
688 | .then(success => {
689 | process.exit(success ? 0 : 1);
690 | })
691 | .catch(error => {
692 | console.error('\n💥 Test suite crashed:', error.message);
693 | console.error(error.stack);
694 | process.exit(1);
695 | });
696 | }
697 |
698 | module.exports = { runTests };
```
--------------------------------------------------------------------------------
/docs/guides/memory-quality-guide.md:
--------------------------------------------------------------------------------
```markdown
1 | # Memory Quality System Guide
2 |
3 | > **Version**: 8.50.0
4 | > **Status**: Production Ready
5 | > **Feature**: Memento-Inspired Quality System (Issue #260)
6 | > **Evaluation**: [Quality System Evaluation Report](https://github.com/doobidoo/mcp-memory-service/wiki/Memory-Quality-System-Evaluation)
7 | >
8 | > **⚠️ IMPORTANT (v8.50.0)**: Fallback mode is **not recommended** due to MS-MARCO architectural limitations (query-document relevance model cannot assess absolute quality). See [Recommended Configuration](#recommended-configuration-v850) for the simpler, more robust approach.
9 |
10 | ## Recommended Configuration (v8.50.0)
11 |
12 | ### Primary Recommendation: Implicit Signals Only
13 |
14 | **✅ For Technical Corpora (RECOMMENDED)**
15 |
16 | If your memory base contains:
17 | - Technical fragments, file paths, references
18 | - Code snippets, CLI output, configuration
19 | - Task lists, tickets, abbreviations
20 | - Short notes and checklists
21 |
22 | ```bash
23 | # Disable AI quality scoring (avoids prose bias)
24 | export MCP_QUALITY_AI_PROVIDER=none
25 |
26 | # Quality based on implicit signals only
27 | export MCP_QUALITY_SYSTEM_ENABLED=true
28 | export MCP_QUALITY_BOOST_ENABLED=false
29 | ```
30 |
31 | **Why Implicit Signals Work Better**:
32 | - ✅ **Access patterns = true quality** - Frequently-used memories are valuable
33 | - ✅ **No prose bias** - Technical fragments treated fairly
34 | - ✅ **Self-learning** - Quality improves based on actual usage
35 | - ✅ **Simpler** - No model loading, zero latency
36 | - ✅ **Works offline** - No external dependencies
37 |
38 | **Implicit Signal Components**:
39 | ```python
40 | quality_score = (
41 | access_frequency × 0.40 + # How often retrieved
42 | recency × 0.30 + # When last accessed
43 | retrieval_ranking × 0.30 # Average position in results
44 | )
45 | ```
46 |
47 | **Real-World Test Results** (v8.50.0):
48 | - DeBERTa average score: 0.209 (median: 0.165)
49 | - Only 4% of memories scored ≥ 0.6 (DeBERTa's "good" threshold)
50 | - Manual review: 72% classified as "low quality" were valid technical references
51 | - Examples misclassified: file paths, Terraform configs, technical abbreviations
52 |
53 | **Conclusion**: DeBERTa trained on Wikipedia/news systematically under-scores technical content.
54 |
55 | ---
56 |
57 | ### Alternative: DeBERTa for Prose-Heavy Corpora
58 |
59 | **For Long-Form Documentation Only**
60 |
61 | If your memory base contains primarily:
62 | - Blog posts, articles, tutorials
63 | - Narrative meeting notes
64 | - Long-form documentation with prose paragraphs
65 |
66 | ```bash
67 | # AI quality scoring with DeBERTa
68 | export MCP_QUALITY_AI_PROVIDER=local
69 | export MCP_QUALITY_LOCAL_MODEL=nvidia-quality-classifier-deberta
70 |
71 | # Lower threshold for technical tolerance
72 | export MCP_QUALITY_DEBERTA_THRESHOLD=0.4 # Or 0.3 for more leniency
73 |
74 | # Combine AI + implicit signals
75 | export MCP_QUALITY_BOOST_ENABLED=true
76 | export MCP_QUALITY_BOOST_WEIGHT=0.3
77 | ```
78 |
79 | **When to Use AI Scoring**:
80 | | Content Type | Use AI? | Reason |
81 | |--------------|---------|--------|
82 | | Narrative documentation | ✅ Yes | DeBERTa trained for prose |
83 | | Blog posts, articles | ✅ Yes | Complete paragraphs |
84 | | Meeting notes (narrative) | ✅ Yes | Story-like structure |
85 | | Technical fragments | ❌ No | Prose bias under-scores |
86 | | File paths, references | ❌ No | Not prose |
87 | | Code snippets, CLI output | ❌ No | Technical, not narrative |
88 | | Task lists, tickets | ❌ No | Fragmented |
89 |
90 | **Expected Distribution** (prose-heavy corpus with 0.4 threshold):
91 | - Low quality (< 0.4): ~20% filtered
92 | - Medium (0.4-0.6): ~40% accepted
93 | - Good (0.6-0.8): ~30% accepted
94 | - Excellent (≥ 0.8): ~10% accepted
95 |
96 | ## Overview
97 |
98 | The **Memory Quality System** transforms MCP Memory Service from static storage to a learning memory system. It automatically evaluates memory quality using AI-driven scoring and uses these scores to improve retrieval precision, consolidation efficiency, and overall system intelligence.
99 |
100 | ### Key Benefits
101 |
102 | - ✅ **40-70% improvement** in retrieval precision (top-5 useful rate: 50% → 70-85%)
103 | - ✅ **Zero cost** with local SLM (privacy-preserving, offline-capable)
104 | - ✅ **Smarter consolidation** - Preserve high-quality memories longer
105 | - ✅ **Quality-boosted search** - Prioritize best memories in results
106 | - ✅ **Automatic learning** - System improves from usage patterns
107 |
108 | ## How It Works
109 |
110 | ### Multi-Tier AI Scoring (Local-First)
111 |
112 | The system evaluates memory quality (0.0-1.0 score) using a multi-tier fallback chain:
113 |
114 | | Tier | Provider | Cost | Latency | Privacy | Default |
115 | |------|----------|------|---------|---------|---------|
116 | | **1** | **Local SLM (ONNX)** | **$0** | **50-100ms** | ✅ Full | ✅ Yes |
117 | | 2 | Groq API | ~$0.30/mo | 900ms | ❌ External | ❌ Opt-in |
118 | | 3 | Gemini API | ~$0.40/mo | 2000ms | ❌ External | ❌ Opt-in |
119 | | 4 | Implicit Signals | $0 | 10ms | ✅ Full | Fallback |
120 |
121 | **Default setup**: Local SLM only (zero cost, full privacy, no external API calls)
122 |
123 | ### Quality Score Components
124 |
125 | ```
126 | quality_score = (
127 | local_slm_score × 0.50 + # Cross-encoder evaluation
128 | implicit_signals × 0.50 # Usage patterns
129 | )
130 |
131 | implicit_signals = (
132 | access_frequency × 0.40 + # How often retrieved
133 | recency × 0.30 + # When last accessed
134 | retrieval_ranking × 0.30 # Average position in results
135 | )
136 | ```
137 |
138 | ## 🎯 Local SLM Models (Tier 1 - Primary)
139 |
140 | **Update (v8.49.0)**: DeBERTa quality classifier is now the default model, eliminating self-matching bias and providing absolute quality assessment.
141 |
142 | ### Model Options
143 |
144 | The quality system supports multiple ONNX models for different use cases:
145 |
146 | #### **NVIDIA DeBERTa Quality Classifier** (Default, Recommended)
147 |
148 | **Model**: `nvidia-quality-classifier-deberta` (450MB)
149 | **Architecture**: 3-class text classifier (Low/Medium/High quality)
150 | **Type**: Absolute quality assessment (query-independent)
151 |
152 | **Key Features**:
153 | - ✅ **Eliminates self-matching bias** - No query needed, evaluates content directly
154 | - ✅ **Absolute quality scores** - Assesses inherent memory quality
155 | - ✅ **Uniform distribution** - More realistic quality spread (mean: 0.60-0.70)
156 | - ✅ **Fewer false positives** - <5% perfect 1.0 scores (vs 20% with MS-MARCO)
157 |
158 | **Performance**:
159 | - CPU: 80-150ms per evaluation
160 | - GPU (CUDA/MPS/DirectML): 20-40ms per evaluation
161 | - Model size: 450MB (downloaded once, cached locally)
162 |
163 | **Scoring Process**:
164 | 1. Tokenize memory content (no query needed)
165 | 2. Run DeBERTa inference (local, private)
166 | 3. Get 3-class probabilities: P(low), P(medium), P(high)
167 | 4. Calculate weighted score: `0.0×P(low) + 0.5×P(medium) + 1.0×P(high)`
168 |
169 | **Usage**:
170 | ```bash
171 | # Default model (no configuration needed)
172 | export MCP_QUALITY_LOCAL_MODEL=nvidia-quality-classifier-deberta # Already default in v8.49.0+
173 |
174 | # Quality boost works better with DeBERTa (more accurate scores)
175 | export MCP_QUALITY_BOOST_ENABLED=true
176 | export MCP_QUALITY_BOOST_WEIGHT=0.3
177 | ```
178 |
179 | **Quality Metrics** (Expected vs MS-MARCO):
180 | | Metric | MS-MARCO (Old) | DeBERTa (New) | Improvement |
181 | |--------|---------------|---------------|-------------|
182 | | Mean Score | 0.469 | 0.60-0.70 | +28-49% |
183 | | Perfect 1.0 Scores | 15-20% | <5% | -60% false positives |
184 | | High Quality (≥0.7) | 32.2% | 40-50% | More accurate |
185 | | Distribution | Bimodal | Uniform | Better spread |
186 |
187 | #### **MS-MARCO Cross-Encoder** (Legacy, Backward Compatible)
188 |
189 | **Model**: `ms-marco-MiniLM-L-6-v2` (23MB)
190 | **Architecture**: Cross-encoder (query-document relevance ranking)
191 | **Type**: Relative relevance assessment (query-dependent)
192 |
193 | **Use Cases**:
194 | - Legacy systems requiring MS-MARCO compatibility
195 | - Relative ranking within search results
196 | - When disk space is extremely limited (<100MB)
197 |
198 | **Known Limitations**:
199 | - ⚠️ **Self-matching bias** - Tag-based queries match content (~25% false positives)
200 | - ⚠️ **Bimodal distribution** - Scores cluster at extremes (0.0 or 1.0)
201 | - ⚠️ **Requires meaningful queries** - Empty queries return 0.0
202 |
203 | **Performance**:
204 | - CPU: 50-100ms per evaluation
205 | - GPU (CUDA/MPS/DirectML): 10-20ms per evaluation
206 | - Model size: 23MB
207 |
208 | **Usage** (opt-in for legacy compatibility):
209 | ```bash
210 | # Override to use MS-MARCO (not recommended)
211 | export MCP_QUALITY_LOCAL_MODEL=ms-marco-MiniLM-L-6-v2
212 | ```
213 |
214 | #### **Fallback Quality Scoring** (Best of Both Worlds) 🆕 v8.50.0
215 |
216 | **Model**: DeBERTa + MS-MARCO hybrid approach
217 | **Architecture**: Threshold-based fallback for optimal quality assessment
218 | **Type**: Absolute quality with technical content rescue
219 |
220 | **Problem Solved**:
221 | DeBERTa has systematic bias toward prose/narrative content over technical documentation:
222 | - **Prose content**: 0.78-0.92 scores (excellent)
223 | - **Technical content**: 0.48-0.60 scores (undervalued)
224 | - **Impact**: 95% of technical memories undervalued in technical databases
225 |
226 | **Solution - Threshold-Based Fallback**:
227 | ```python
228 | # Always score with DeBERTa first
229 | deberta_score = score_with_deberta(content)
230 |
231 | # If DeBERTa confident (high score), use it
232 | if deberta_score >= DEBERTA_THRESHOLD:
233 | return deberta_score # MS-MARCO not consulted (fast path)
234 |
235 | # DeBERTa scored low - check if MS-MARCO thinks it's good (technical content)
236 | ms_marco_score = score_with_ms_marco(content)
237 | if ms_marco_score >= MS_MARCO_THRESHOLD:
238 | return ms_marco_score # Rescue technical content
239 |
240 | # Both agree it's low quality
241 | return deberta_score
242 | ```
243 |
244 | **Key Features**:
245 | - ✅ **Signal Preservation** - No averaging dilution, uses strongest signal
246 | - ✅ **DeBERTa Primary** - Eliminates self-matching bias for prose
247 | - ✅ **MS-MARCO Rescue** - Catches undervalued technical content
248 | - ✅ **Performance Optimized** - MS-MARCO only runs when DeBERTa scores low (~60% of memories)
249 | - ✅ **Simple Configuration** - Just thresholds, no complex weights
250 |
251 | **Expected Results**:
252 | | Content Type | DeBERTa-Only | Fallback Mode | Improvement |
253 | |--------------|--------------|---------------|-------------|
254 | | **Technical content** | 0.48-0.60 | 0.70-0.80 | +45-65% ✅ |
255 | | **Prose content** | 0.78-0.92 | 0.78-0.92 | No change ✅ |
256 | | **High quality (≥0.7)** | 0.4% (17 memories) | 20-30% (800-1200) | 50-75x ✅ |
257 |
258 | **Performance**:
259 | | Scenario | DeBERTa-Only | Fallback | Time |
260 | |----------|--------------|----------|------|
261 | | **High prose** (~40% of memories) | DeBERTa only | DeBERTa only | 115ms ⚡ |
262 | | **Low technical** (~35% of memories) | DeBERTa only | DeBERTa + MS-MARCO rescue | 155ms |
263 | | **Low garbage** (~25% of memories) | DeBERTa only | DeBERTa + MS-MARCO both low | 155ms |
264 | | **Average** | 115ms | 139ms | +21% acceptable overhead |
265 |
266 | **Configuration**:
267 | ```bash
268 | # Enable fallback mode (requires both models)
269 | export MCP_QUALITY_FALLBACK_ENABLED=true
270 | export MCP_QUALITY_LOCAL_MODEL="nvidia-quality-classifier-deberta,ms-marco-MiniLM-L-6-v2"
271 |
272 | # Thresholds (recommended defaults)
273 | export MCP_QUALITY_DEBERTA_THRESHOLD=0.6 # DeBERTa confidence threshold
274 | export MCP_QUALITY_MSMARCO_THRESHOLD=0.7 # MS-MARCO rescue threshold
275 |
276 | # Quality boost works well with fallback
277 | export MCP_QUALITY_BOOST_ENABLED=true
278 | export MCP_QUALITY_BOOST_WEIGHT=0.3
279 | ```
280 |
281 | **Configuration Tuning**:
282 | ```bash
283 | # Technical-heavy systems (more MS-MARCO rescues)
284 | export MCP_QUALITY_DEBERTA_THRESHOLD=0.5 # Lower threshold → more rescues
285 | export MCP_QUALITY_MSMARCO_THRESHOLD=0.6 # Lower bar for technical content
286 |
287 | # Quality-focused (stricter standards)
288 | export MCP_QUALITY_DEBERTA_THRESHOLD=0.7 # Higher threshold → DeBERTa must be very confident
289 | export MCP_QUALITY_MSMARCO_THRESHOLD=0.8 # Higher bar for rescue
290 |
291 | # Balanced (recommended default)
292 | export MCP_QUALITY_DEBERTA_THRESHOLD=0.6
293 | export MCP_QUALITY_MSMARCO_THRESHOLD=0.7
294 | ```
295 |
296 | **Decision Tracking**:
297 | Fallback mode stores detailed decision metadata:
298 | ```json
299 | {
300 | "quality_score": 0.78,
301 | "quality_provider": "fallback_deberta-msmarco",
302 | "quality_components": {
303 | "decision": "ms_marco_rescue",
304 | "deberta_score": 0.52,
305 | "ms_marco_score": 0.78,
306 | "final_score": 0.78
307 | }
308 | }
309 | ```
310 |
311 | **Decision Types**:
312 | - `deberta_confident`: DeBERTa ≥ threshold, used DeBERTa (fast path, ~40%)
313 | - `ms_marco_rescue`: DeBERTa low, MS-MARCO ≥ threshold, used MS-MARCO (~35%)
314 | - `both_low`: Both below thresholds, used DeBERTa (~25%)
315 | - `deberta_only`: MS-MARCO not loaded, DeBERTa only
316 | - `ms_marco_failed`: MS-MARCO error, used DeBERTa
317 |
318 | **Re-scoring Existing Memories**:
319 | ```bash
320 | # Dry-run (preview changes)
321 | python scripts/quality/rescore_fallback.py
322 |
323 | # Execute re-scoring
324 | python scripts/quality/rescore_fallback.py --execute
325 |
326 | # Custom thresholds
327 | python scripts/quality/rescore_fallback.py --execute \
328 | --deberta-threshold 0.5 \
329 | --msmarco-threshold 0.6
330 |
331 | # Expected time: 3-5 minutes for 4,000-5,000 memories
332 | # Creates backup automatically in ~/backups/mcp-memory-service/
333 | ```
334 |
335 | **When to Use Fallback Mode**:
336 | | Use Case | Recommended Mode |
337 | |----------|-----------------|
338 | | **Technical documentation** | ✅ Fallback (rescues undervalued content) |
339 | | **Mixed content** (code + prose) | ✅ Fallback (best of both) |
340 | | **Prose-only** (creative writing) | DeBERTa-only (faster, same quality) |
341 | | **Strict quality** (academic) | DeBERTa-only (no rescue for low scores) |
342 | | **Legacy compatibility** | MS-MARCO-only (if required) |
343 |
344 | ### GPU Acceleration (Automatic)
345 |
346 | Both models support automatic GPU detection:
347 | - **CUDA** (NVIDIA GPUs)
348 | - **CoreML/MPS** (Apple Silicon M1/M2/M3)
349 | - **DirectML** (Windows DirectX)
350 | - **ROCm** (AMD GPUs on Linux)
351 | - **CPU** (fallback, always works)
352 |
353 | ```bash
354 | # GPU detection is automatic
355 | export MCP_QUALITY_LOCAL_DEVICE=auto # Default (auto-detects best device)
356 |
357 | # Or force specific device
358 | export MCP_QUALITY_LOCAL_DEVICE=cpu # Force CPU
359 | export MCP_QUALITY_LOCAL_DEVICE=cuda # Force CUDA (NVIDIA)
360 | export MCP_QUALITY_LOCAL_DEVICE=mps # Force MPS (Apple Silicon)
361 | ```
362 |
363 | ### Migration from MS-MARCO to DeBERTa
364 |
365 | If you're upgrading from v8.48.x or earlier and want to re-evaluate existing memories:
366 |
367 | ```bash
368 | # Export DeBERTa model (one-time, downloads 450MB)
369 | python scripts/quality/export_deberta_onnx.py
370 |
371 | # Re-evaluate all memories with DeBERTa
372 | python scripts/quality/migrate_to_deberta.py
373 |
374 | # Verify new distribution
375 | curl -ks https://127.0.0.1:8000/api/quality/distribution | python3 -m json.tool
376 | ```
377 |
378 | **Migration preserves**:
379 | - Original MS-MARCO scores (in `quality_migration` metadata)
380 | - Access patterns and timestamps
381 | - User ratings and feedback
382 |
383 | **Migration time**: ~10-20 minutes for 4,000-5,000 memories (depends on CPU/GPU)
384 |
385 | ## Installation & Setup
386 |
387 | ### 1. Basic Setup (Local SLM Only)
388 |
389 | **Zero configuration required** - The quality system works out of the box with local SLM:
390 |
391 | ```bash
392 | # Install MCP Memory Service (if not already installed)
393 | pip install mcp-memory-service
394 |
395 | # Quality system is enabled by default with local SLM
396 | # No API keys needed, no external calls
397 | ```
398 |
399 | ### 2. Optional: Cloud APIs (Opt-In)
400 |
401 | If you want cloud-based scoring (Groq or Gemini):
402 |
403 | ```bash
404 | # Enable Groq API (fast, cheap)
405 | export GROQ_API_KEY="your-groq-api-key"
406 | export MCP_QUALITY_AI_PROVIDER=groq # or "auto" to try all tiers
407 |
408 | # Enable Gemini API (Google)
409 | export GOOGLE_API_KEY="your-gemini-api-key"
410 | export MCP_QUALITY_AI_PROVIDER=gemini
411 | ```
412 |
413 | ### 3. Configuration Options
414 |
415 | ```bash
416 | # Quality System Core
417 | export MCP_QUALITY_SYSTEM_ENABLED=true # Default: true
418 | export MCP_QUALITY_AI_PROVIDER=local # local|groq|gemini|auto|none
419 |
420 | # Local SLM Configuration (Tier 1)
421 | export MCP_QUALITY_LOCAL_MODEL=ms-marco-MiniLM-L-6-v2 # Model name
422 | export MCP_QUALITY_LOCAL_DEVICE=auto # auto|cpu|cuda|mps|directml
423 |
424 | # Quality-Boosted Search (Opt-In)
425 | export MCP_QUALITY_BOOST_ENABLED=false # Default: false (opt-in)
426 | export MCP_QUALITY_BOOST_WEIGHT=0.3 # 0.0-1.0 (30% quality, 70% semantic)
427 |
428 | # Quality-Based Retention (Consolidation)
429 | export MCP_QUALITY_RETENTION_HIGH=365 # Days for quality ≥0.7
430 | export MCP_QUALITY_RETENTION_MEDIUM=180 # Days for quality 0.5-0.7
431 | export MCP_QUALITY_RETENTION_LOW_MIN=30 # Min days for quality <0.5
432 | export MCP_QUALITY_RETENTION_LOW_MAX=90 # Max days for quality <0.5
433 | ```
434 |
435 | ## Using the Quality System
436 |
437 | ### 1. Automatic Quality Scoring
438 |
439 | Quality scores are calculated automatically when memories are retrieved:
440 |
441 | ```bash
442 | # Normal retrieval - quality scoring happens in background
443 | claude /memory-recall "what did I work on yesterday"
444 |
445 | # Quality score is updated in metadata (non-blocking)
446 | ```
447 |
448 | ### 2. Manual Rating (Optional)
449 |
450 | Override AI scores with manual ratings:
451 |
452 | ```bash
453 | # Rate a memory (MCP tool)
454 | rate_memory(
455 | content_hash="abc123...",
456 | rating=1, # -1 (bad), 0 (neutral), 1 (good)
457 | feedback="This was very helpful!"
458 | )
459 |
460 | # Manual ratings weighted 60%, AI scores weighted 40%
461 | ```
462 |
463 | **HTTP API**:
464 | ```bash
465 | curl -X POST http://127.0.0.1:8000/api/quality/memories/{hash}/rate \
466 | -H "Content-Type: application/json" \
467 | -d '{"rating": 1, "feedback": "Helpful!"}'
468 | ```
469 |
470 | ### 3. Quality-Boosted Search
471 |
472 | Enable quality-based reranking for better results:
473 |
474 | **Method 1: Global Configuration**
475 | ```bash
476 | export MCP_QUALITY_BOOST_ENABLED=true
477 | claude /memory-recall "search query" # Uses quality boost
478 | ```
479 |
480 | **Method 2: Per-Query (MCP Tool)**
481 | ```bash
482 | # Search with quality boost (MCP tool)
483 | retrieve_with_quality_boost(
484 | query="search query",
485 | n_results=10,
486 | quality_weight=0.3 # 30% quality, 70% semantic
487 | )
488 | ```
489 |
490 | **Algorithm**:
491 | 1. Over-fetch 3× candidates (30 results for top 10)
492 | 2. Rerank by: `0.7 × semantic_similarity + 0.3 × quality_score`
493 | 3. Return top N results
494 |
495 | **Performance**: <100ms total (50ms semantic search + 20ms reranking + 30ms quality scoring)
496 |
497 | ### 4. View Quality Metrics
498 |
499 | **MCP Tool**:
500 | ```bash
501 | get_memory_quality(content_hash="abc123...")
502 |
503 | # Returns:
504 | # - quality_score: Current composite score (0.0-1.0)
505 | # - quality_provider: Which tier scored it (ONNXRankerModel, etc.)
506 | # - access_count: Number of retrievals
507 | # - last_accessed_at: Last access timestamp
508 | # - ai_scores: Historical AI evaluation scores
509 | # - user_rating: Manual rating if present
510 | ```
511 |
512 | **HTTP API**:
513 | ```bash
514 | curl http://127.0.0.1:8000/api/quality/memories/{hash}
515 | ```
516 |
517 | ### 5. Quality Analytics
518 |
519 | **MCP Tool**:
520 | ```bash
521 | analyze_quality_distribution(min_quality=0.0, max_quality=1.0)
522 |
523 | # Returns:
524 | # - total_memories: Total count
525 | # - high_quality_count: Score ≥0.7
526 | # - medium_quality_count: 0.5 ≤ score < 0.7
527 | # - low_quality_count: Score < 0.5
528 | # - average_score: Mean quality score
529 | # - provider_breakdown: Count by provider
530 | # - top_10_memories: Highest scoring
531 | # - bottom_10_memories: Lowest scoring
532 | ```
533 |
534 | **Dashboard** (http://127.0.0.1:8000/):
535 | - Quality badges on all memory cards (color-coded by tier)
536 | - Analytics view with distribution charts
537 | - Provider breakdown pie chart
538 | - Top/bottom performers lists
539 |
540 | ## Quality-Based Memory Management
541 |
542 | ### 1. Quality-Based Forgetting (Consolidation)
543 |
544 | High-quality memories are preserved longer during consolidation:
545 |
546 | | Quality Tier | Score Range | Retention Period |
547 | |--------------|-------------|------------------|
548 | | **High** | ≥0.7 | 365 days inactive |
549 | | **Medium** | 0.5-0.7 | 180 days inactive |
550 | | **Low** | <0.5 | 30-90 days inactive (scaled by score) |
551 |
552 | **How it works**:
553 | - Weekly consolidation scans inactive memories
554 | - Applies quality-based thresholds
555 | - Archives low-quality memories sooner
556 | - Preserves high-quality memories longer
557 |
558 | ### 2. Quality-Weighted Decay
559 |
560 | High-quality memories decay slower in relevance scoring:
561 |
562 | ```
563 | decay_multiplier = 1.0 + (quality_score × 0.5)
564 | # High quality (0.9): 1.45× multiplier
565 | # Medium quality (0.5): 1.25× multiplier
566 | # Low quality (0.2): 1.10× multiplier
567 |
568 | final_relevance = base_relevance × decay_multiplier
569 | ```
570 |
571 | **Effect**: High-quality memories stay relevant longer in search results.
572 |
573 | ## Privacy & Cost
574 |
575 | ### Privacy Modes
576 |
577 | | Mode | Configuration | Privacy | Cost |
578 | |------|---------------|---------|------|
579 | | **Local Only** | `MCP_QUALITY_AI_PROVIDER=local` | ✅ Full (no external calls) | $0 |
580 | | **Hybrid** | `MCP_QUALITY_AI_PROVIDER=auto` | ⚠️ Cloud fallback | ~$0.30/mo |
581 | | **Cloud** | `MCP_QUALITY_AI_PROVIDER=groq` | ❌ External API | ~$0.30/mo |
582 | | **Implicit Only** | `MCP_QUALITY_AI_PROVIDER=none` | ✅ Full (no AI) | $0 |
583 |
584 | ### Cost Comparison (3500 memories, 100 retrievals/day)
585 |
586 | | Provider | Monthly Cost | Notes |
587 | |----------|--------------|-------|
588 | | **Local SLM** | **$0** | Free forever, runs locally |
589 | | Groq (Kimi K2) | ~$0.30-0.50 | Fast, good quality |
590 | | Gemini Flash | ~$0.40-0.60 | Slower, free tier available |
591 | | Implicit Only | $0 | No AI scoring, usage patterns only |
592 |
593 | **Recommendation**: Use default local SLM (zero cost, full privacy, fast).
594 |
595 | ## Performance Benchmarks
596 |
597 | | Operation | Latency | Notes |
598 | |-----------|---------|-------|
599 | | **Local SLM Scoring (CPU)** | 50-100ms | Per memory evaluation |
600 | | **Local SLM Scoring (GPU)** | 10-20ms | With CUDA/MPS/DirectML |
601 | | **Quality-Boosted Search** | <100ms | Over-fetch + rerank |
602 | | **Implicit Signals** | <10ms | Always fast |
603 | | **Quality Metadata Update** | <5ms | Storage backend write |
604 |
605 | **Target Metrics**:
606 | - Quality calculation overhead: <10ms
607 | - Search latency with boost: <100ms total
608 | - No user-facing blocking (async scoring)
609 |
610 | ## Troubleshooting
611 |
612 | ### Local SLM Not Working
613 |
614 | **Symptom**: `quality_provider: ImplicitSignalsEvaluator` (should be `ONNXRankerModel`)
615 |
616 | **Fixes**:
617 | 1. Check ONNX Runtime installed:
618 | ```bash
619 | pip install onnxruntime
620 | ```
621 |
622 | 2. Check model downloaded:
623 | ```bash
624 | ls ~/.cache/mcp_memory/onnx_models/ms-marco-MiniLM-L-6-v2/
625 | # Should contain: model.onnx, tokenizer.json
626 | ```
627 |
628 | 3. Check logs for errors:
629 | ```bash
630 | tail -f logs/mcp_memory_service.log | grep quality
631 | ```
632 |
633 | ### Quality Scores Always 0.5
634 |
635 | **Symptom**: All memories have `quality_score: 0.5` (neutral default)
636 |
637 | **Cause**: Quality scoring not triggered yet (memories haven't been retrieved)
638 |
639 | **Fix**: Retrieve memories to trigger scoring:
640 | ```bash
641 | claude /memory-recall "any search query"
642 | # Quality scoring happens in background after retrieval
643 | ```
644 |
645 | ### GPU Not Detected
646 |
647 | **Symptom**: Local SLM uses CPU despite having GPU
648 |
649 | **Fixes**:
650 | 1. Install GPU-enabled ONNX Runtime:
651 | ```bash
652 | # NVIDIA CUDA
653 | pip install onnxruntime-gpu
654 |
655 | # DirectML (Windows)
656 | pip install onnxruntime-directml
657 | ```
658 |
659 | 2. Force device selection:
660 | ```bash
661 | export MCP_QUALITY_LOCAL_DEVICE=cuda # or mps, directml
662 | ```
663 |
664 | ### Quality Boost Not Working
665 |
666 | **Symptom**: Search results don't show quality reranking
667 |
668 | **Checks**:
669 | 1. Verify enabled:
670 | ```bash
671 | echo $MCP_QUALITY_BOOST_ENABLED # Should be "true"
672 | ```
673 |
674 | 2. Use explicit MCP tool:
675 | ```bash
676 | retrieve_with_quality_boost(query="test", quality_weight=0.5)
677 | ```
678 |
679 | 3. Check debug info in results:
680 | ```python
681 | result.debug_info['reranked'] # Should be True
682 | result.debug_info['quality_score'] # Should exist
683 | ```
684 |
685 | ## Best Practices
686 |
687 | ### 1. Start with Defaults (Updated for v8.48.3)
688 |
689 | Use local SLM (default) for:
690 | - Zero cost
691 | - Full privacy
692 | - Offline capability
693 | - **Good for relative ranking** (not absolute quality assessment)
694 |
695 | **Important**: Local SLM scores should be **combined with implicit signals** (access patterns, recency) for best results. Do not rely on ONNX scores alone.
696 |
697 | ### 2. Enable Quality Boost Gradually
698 |
699 | ```bash
700 | # Week 1: Collect quality scores (boost disabled)
701 | export MCP_QUALITY_BOOST_ENABLED=false
702 |
703 | # Week 2: Test with low weight
704 | export MCP_QUALITY_BOOST_ENABLED=true
705 | export MCP_QUALITY_BOOST_WEIGHT=0.2 # 20% quality
706 |
707 | # Week 3+: Increase if helpful
708 | export MCP_QUALITY_BOOST_WEIGHT=0.3 # 30% quality (recommended)
709 | ```
710 |
711 | ### 3. Monitor Quality Distribution (With Caution)
712 |
713 | Check analytics regularly, but interpret with awareness of limitations:
714 | ```bash
715 | analyze_quality_distribution()
716 |
717 | # Current observed distribution (v8.48.3):
718 | # - High quality (≥0.7): 32.2% (includes ~25% false positives from self-matching)
719 | # - Medium quality (0.5-0.7): 27.4%
720 | # - Low quality (<0.5): 40.4%
721 |
722 | # Ideal distribution (future with hybrid scoring):
723 | # - High quality (≥0.7): 20-30% of memories
724 | # - Medium quality (0.5-0.7): 50-60%
725 | # - Low quality (<0.5): 10-20%
726 | ```
727 |
728 | **Warning**: High scores may not always indicate high quality due to self-matching bias. Validate manually before making retention decisions.
729 |
730 | ### 4. Manual Rating for Edge Cases
731 |
732 | Rate important memories manually:
733 | ```bash
734 | # After finding a very helpful memory
735 | rate_memory(content_hash="abc123...", rating=1, feedback="Critical info!")
736 |
737 | # After finding unhelpful memory
738 | rate_memory(content_hash="def456...", rating=-1, feedback="Outdated")
739 | ```
740 |
741 | Manual ratings weighted 60%, AI scores 40%.
742 |
743 | ### 5. Periodic Review (Updated for v8.48.3)
744 |
745 | Monthly checklist:
746 | - [ ] Check quality distribution (analytics dashboard)
747 | - [ ] **Manually validate** top 10 performers (verify genuinely helpful, not just self-matching bias)
748 | - [ ] **Manually validate** bottom 10 before deletion (may be low-quality or just poor matches)
749 | - [ ] Verify provider breakdown (target: 75%+ local SLM)
750 | - [ ] Check average quality score (current: 0.469, target with hybrid: 0.6+)
751 | - [ ] **Review Issue #268** progress on quality improvements
752 |
753 | ## Advanced Configuration
754 |
755 | ### Custom Retention Policy
756 |
757 | ```bash
758 | # Conservative: Preserve longer
759 | export MCP_QUALITY_RETENTION_HIGH=730 # 2 years for high quality
760 | export MCP_QUALITY_RETENTION_MEDIUM=365 # 1 year for medium
761 | export MCP_QUALITY_RETENTION_LOW_MIN=90 # 90 days minimum for low
762 |
763 | # Aggressive: Archive sooner
764 | export MCP_QUALITY_RETENTION_HIGH=180 # 6 months for high
765 | export MCP_QUALITY_RETENTION_MEDIUM=90 # 3 months for medium
766 | export MCP_QUALITY_RETENTION_LOW_MIN=14 # 2 weeks minimum for low
767 | ```
768 |
769 | ### Custom Quality Boost Weight
770 |
771 | ```bash
772 | # Semantic-first (default)
773 | export MCP_QUALITY_BOOST_WEIGHT=0.3 # 30% quality, 70% semantic
774 |
775 | # Balanced
776 | export MCP_QUALITY_BOOST_WEIGHT=0.5 # 50% quality, 50% semantic
777 |
778 | # Quality-first
779 | export MCP_QUALITY_BOOST_WEIGHT=0.7 # 70% quality, 30% semantic
780 | ```
781 |
782 | **Recommendation**: Start with 0.3, increase if quality boost improves results.
783 |
784 | ### Hybrid Cloud Strategy
785 |
786 | Use local SLM primarily, cloud APIs as fallback:
787 |
788 | ```bash
789 | export MCP_QUALITY_AI_PROVIDER=auto # Try all available tiers
790 | export GROQ_API_KEY="your-key" # Groq as Tier 2 fallback
791 | ```
792 |
793 | **Behavior**:
794 | 1. Try local SLM (99% success rate)
795 | 2. If fails, try Groq API
796 | 3. If fails, try Gemini API
797 | 4. Ultimate fallback: Implicit signals only
798 |
799 | ## Success Metrics (Phase 1 Targets)
800 |
801 | From Issue #260 and #261 roadmap:
802 |
803 | | Metric | Target | Measurement |
804 | |--------|--------|-------------|
805 | | **Retrieval Precision** | >70% useful (top-5) | Up from ~50% baseline |
806 | | **Quality Coverage** | >30% memories scored | Within 3 months |
807 | | **Quality Distribution** | 20-30% high-quality | Pareto principle |
808 | | **Search Latency** | <100ms with boost | SQLite-vec backend |
809 | | **Monthly Cost** | <$0.50 or $0 | Groq API or local SLM |
810 | | **Local SLM Usage** | >95% of scoring | Tier 1 success rate |
811 |
812 | ## FAQ
813 |
814 | ### Q: Do I need API keys for the quality system?
815 |
816 | **A**: No! The default local SLM works with zero configuration, no API keys, and no external calls.
817 |
818 | ### Q: How much does it cost?
819 |
820 | **A**: $0 with the default local SLM. Optional cloud APIs cost ~$0.30-0.50/month for typical usage.
821 |
822 | ### Q: Does quality scoring slow down searches?
823 |
824 | **A**: No. Scoring happens asynchronously in the background. Quality-boosted search adds <20ms overhead.
825 |
826 | ### Q: Can I disable the quality system?
827 |
828 | **A**: Yes, set `MCP_QUALITY_SYSTEM_ENABLED=false`. System works normally without quality scores.
829 |
830 | ### Q: How accurate is the local SLM?
831 |
832 | **A**: 80%+ correlation with human quality ratings. Good enough for ranking and retention decisions.
833 |
834 | ### Q: What if the local SLM fails to download?
835 |
836 | **A**: System falls back to implicit signals (access patterns). No failures, degraded gracefully.
837 |
838 | ### Q: Can I use my own quality scoring model?
839 |
840 | **A**: Yes! Implement the `QualityEvaluator` interface and configure via `MCP_QUALITY_AI_PROVIDER`.
841 |
842 | ### Q: Does this work offline?
843 |
844 | **A**: Yes! Local SLM works fully offline. No internet required for quality scoring.
845 |
846 | ## Related Documentation
847 |
848 | - [Issue #260](https://github.com/doobidoo/mcp-memory-service/issues/260) - Quality System Specification
849 | - [Issue #261](https://github.com/doobidoo/mcp-memory-service/issues/261) - Roadmap (Quality → Agentic RAG)
850 | - [Consolidation Guide](./memory-consolidation-guide.md) - Detailed consolidation documentation
851 | - [API Reference](../api/quality-endpoints.md) - HTTP API documentation
852 |
853 | ## Changelog
854 |
855 | **v8.48.3** (2025-12-08):
856 | - **Documentation Update**: Added comprehensive ONNX limitations section
857 | - Documented self-matching bias and bimodal distribution issues
858 | - Updated best practices with manual validation recommendations
859 | - Added performance benchmarks from evaluation (4,762 memories)
860 | - Linked to [Quality System Evaluation Report](https://github.com/doobidoo/mcp-memory-service/wiki/Memory-Quality-System-Evaluation)
861 | - Created [Issue #268](https://github.com/doobidoo/mcp-memory-service/issues/268) for Phase 2 improvements
862 |
863 | **v8.45.0** (2025-01-XX):
864 | - Initial release of Memory Quality System
865 | - Local SLM (ONNX) as primary tier
866 | - Quality-based forgetting in consolidation
867 | - Quality-boosted search with reranking
868 | - Dashboard UI with quality badges and analytics
869 | - Comprehensive MCP tools and HTTP API
870 |
871 | ---
872 |
873 | **Need help?** Open an issue at https://github.com/doobidoo/mcp-memory-service/issues
874 |
875 | **Quality System Improvements**: See [Issue #268](https://github.com/doobidoo/mcp-memory-service/issues/268) for planned enhancements
876 |
```