ai-memory-mcp
by sscctv
README.md
# MCP Memory Suite
[](https://opensource.org/licenses/MIT)
[](https://www.python.org/downloads/)
[](#development)
[](#development)
A comprehensive memory plugin for AI coding assistants and personal knowledge management, built on the **Model Context Protocol (MCP)**. It gives AI assistants (Cursor, Claude, and any MCP-compatible client) persistent, intelligent memory that grows smarter with use -- combining neuroscience-inspired retrieval, Hebbian learning, and governance with a full suite of personal data management modules including habits, experiences, knowledge, accounts, and lifestyle tracking.
The system is organized into two major subsystems:
1. **Core AI Memory System** -- 9 MCP tools for storing, searching, and governing memories that help AI coding assistants recall project context across sessions. Uses a three-layer retrieval pipeline (L1 vector + BM25, L2 PageRank spreading activation, L3 context packing), three-factor reinforcement weights (retrieval frequency, adoption rate, feedback), Hebbian learning, Ebbinghaus decay, and automated codebase bootstrap scanning.
2. **Personal Memory Modules** -- 15 MCP tools for managing personal data: encrypted account vault, habit tracking with streaks, experience recording with emotion journeys, knowledge management with SM-2 spaced repetition, lifestyle information with PII masking, and a unified cross-module manager.
---
## Table of Contents
- [Features Overview](#features-overview)
- [Core AI Memory System](#core-ai-memory-system)
- [Personal Memory Modules](#personal-memory-modules)
- [Installation](#installation)
- [Configuration](#configuration)
- [Usage -- MCP Server Setup](#usage--mcp-server-setup)
- [MCP Tools Reference](#mcp-tools-reference)
- [Core Memory Tools (9)](#core-memory-tools-9)
- [Personal Memory Tools (15)](#personal-memory-tools-15)
- [Security](#security)
- [Development](#development)
- [Project Structure](#project-structure)
- [License](#license)
---
## Features Overview
### Core AI Memory System
The core system provides long-term, structured memory for AI coding assistants. Instead of storing raw dialogue, it extracts concise facts (30--50 tokens) with rich metadata that drive intelligent retrieval and lifecycle management.
| Feature | Description |
|---|---|
| **Three-Layer Retrieval Pipeline** | L1: parallel vector (ChromaDB) + BM25 recall with fusion scoring. L2: Personalized PageRank spreading activation across the Hebbian connection graph. L3: three-factor weighted scoring with Ebbinghaus temporal decay and token-budget packing. |
| **Three-Factor Reinforcement Weights** | `W = 1 + w_retrieval * f_retrieval + w_adoption * adoption_rate + w_feedback * f_feedback`. Retrieval frequency (Hebbian), adoption rate (dopamine), and time-decayed feedback (neuromodulator) combine to amplify or attenuate each memory's influence. |
| **Hebbian Learning** | Memories that are co-activated during retrieval have their connection weights strengthened. Connections decay passively over time; weak edges below a threshold are pruned. |
| **Memory Lifecycle** | Episodic memories are compressed into semantic summaries after a configurable period. Low-weight memories enter a forget queue. Old memories are archived to cold storage. |
| **Governance Engine** | Automated audits detect redundant memories (high embedding similarity), contradictory memories (conflicting keyword pairs), and weak graph edges. A composite health score (0--100) summarizes store quality across five dimensions. |
| **Codebase Bootstrap** | On first project connection, a scanner extracts initial memories from config files (tech stack, build tools, directory structure, entry points) to provide immediate value before conversational memories accumulate. |
| **Write-Time Deduplication** | When a new memory's cosine similarity to an existing one exceeds the threshold, they are automatically merged (content concatenated, tags unioned, importance maximized, access count incremented). |
| **LRU Hot Cache** | An L1 hot cache with configurable TTL sits in front of the retrieval pipeline for frequently accessed memories. |
| **Project Isolation** | Memories are scoped by `project_id`, enabling multi-project workflows without cross-contamination. |
### Personal Memory Modules
Six interconnected modules for personal data management, each with its own SQLite store and privacy controls:
| Module | Description |
|---|---|
| **Account Vault** | Encrypted credential storage with Argon2id key derivation and Fernet (AES-128-CBC + HMAC-SHA256) encryption. Supports 11 account categories, password history, security questions, API keys, recovery codes, 2FA flags, password expiry tracking, and strength evaluation. Auto-mode (passwordless) for local use; upgradeable to password mode. All display output is masked. |
| **Habit Tracking** | 6 frequency types (daily, weekly, Mon--Fri, specific days, every-N-days, monthly), grace days, automatic streak computation (current + longest), completion rates, mood ratings with notes, categories, and favorites. |
| **Experience Recording** | 16 experience categories, 7 sentiment levels, emotion journey tracking (chronological emotion data points), growth outcomes (reframing failures as learning), follow-up reflections with sentiment shift, milestone classification for narrative identity, and guided reflection prompts. |
| **Knowledge Management** | SM-2 spaced repetition algorithm with ease factors and intervals. 13 knowledge types, 6 mastery levels (Bloom's taxonomy), source provenance, application logging, knowledge graph (related/prerequisite/derived cards), confidence scores, and automatic decay risk calculation. |
| **Lifestyle Information** | Preferences with evolution tracking (strength changes over time), daily/weekly routines with energy and mood tracking, contacts with relationship dynamics, and addresses with emotional associations. PII masking on all sensitive fields. |
| **PersonalMemoryManager** | Unified facade with cross-module linking (5 link types: related, supports, inspired_by, contradicts, evolved_into), unified search across all modules, aggregate statistics, and due-item aggregation (overdue habits, due reviews, high decay risk). |
---
## Installation
### Prerequisites
- Python 3.10 or later
- pip
### Install from source
```bash
git clone https://github.com/sscctv/mcp-memory-suite.git
cd mcp-memory-suite
pip install .
```
For local embedding model support (offline vector embeddings via `sentence-transformers`):
```bash
pip install ".[local-embedding]"
```
For development dependencies:
```bash
pip install ".[dev]"
```
### Dependencies
| Package | Purpose |
|---|---|
| `numpy` | Numerical operations for embeddings and similarity computation |
| `chromadb` | Vector database for dense retrieval |
| `rank-bm25` | BM25 keyword search for sparse retrieval |
| `scipy` | Scientific computing support |
| `mcp` | Model Context Protocol server library |
| `cryptography` | Argon2id KDF and Fernet encryption (personal memory vault) |
| `pyyaml` | Configuration file parsing |
| `sentence-transformers` *(optional)* | Local embedding model (`all-MiniLM-L6-v2`) |
After installation, the `ai-memory-mcp` command is available on your PATH.
---
## Configuration
The server reads `config.yaml` from the current directory, the project root, or `~/.ai-memory/config.yaml`. If no file is found, built-in defaults are used.
### Full `config.yaml` Reference
```yaml
# ── Storage ──────────────────────────────────────────────
storage:
sqlite_path: "~/.ai-memory/memory.db" # SQLite database for core memories
chroma_path: "~/.ai-memory/chroma" # ChromaDB directory for vector storage
wal_mode: true # SQLite WAL mode for concurrent reads
# ── L1 Hot Cache ─────────────────────────────────────────
cache:
max_size: 100 # LRU cache capacity (number of entries)
ttl_seconds: 3600 # Cache entry TTL (1 hour)
# ── Embedding ────────────────────────────────────────────
embedding:
api: # Primary: OpenAI API (higher quality)
enabled: false
model: "text-embedding-3-small"
base_url: "https://api.openai.com/v1"
api_key_env: "OPENAI_API_KEY" # Reads from environment variable
dimension: 1536
local: # Fallback: local model (offline)
model: "all-MiniLM-L6-v2"
dimension: 384
cache_dir: "~/.ai-memory/models"
hash_fallback: # Last resort: hash-based embedding
dimension: 256
# ── Three-Factor Reinforcement Weights ───────────────────
weights:
w_retrieval: 0.3 # Call frequency weight (Hebbian)
w_adoption: 0.4 # Adoption rate weight (Dopamine)
w_feedback: 0.3 # Feedback score weight (Modulator)
max_access_count: 100 # Log compression denominator
feedback_half_life_days: 14 # Feedback decay half-life
# ── Decay ────────────────────────────────────────────────
decay:
confidence_lambda: 0.03 # Ebbinghaus decay rate (~23-day half-life)
hebbian_decay: 0.99 # Hebbian connection decay per update
forget_threshold: 0.1 # Weight below this enters forget queue
# ── Retrieval ────────────────────────────────────────────
retrieval:
l1_top_k: 5 # L1 seed node count
l2_max_expansion: 10 # L2 expansion candidate limit
l3_max_results: 10 # L3 final result limit
pagerank:
damping: 0.5 # Personalized PageRank damping factor
max_iterations: 3 # PageRank iterations
min_activation: 0.01 # Stop spreading below this activation
fusion:
vector_weight: 0.5 # Dense retrieval weight
bm25_weight: 0.3 # Sparse retrieval weight
graph_weight: 0.2 # Graph expansion weight
dedup_threshold: 0.85 # Similarity above this triggers merge
# ── Token Budget ─────────────────────────────────────────
token:
default_budget: 2000 # Default token budget for context
cold_start_budget: 800 # When memory count < 50
min_budget: 500
max_budget: 3000
# ── Lifecycle ────────────────────────────────────────────
lifecycle:
compress_after_days: 7 # Compress episodic memories after N days
archive_after_days: 30 # Move to archive after N days
audit_interval_days: 7 # Weekly audit
decay_interval_hours: 24 # Daily decay
# ── Governance ───────────────────────────────────────────
governance:
auto_merge_threshold: 0.90 # Auto-merge if similarity above this
auto_delete_criteria:
min_access_count: 0
max_importance: 0.5
require_negative_feedback: true
weak_edge_threshold: 0.05 # Prune Hebbian edges below this
# ── MCP Server ───────────────────────────────────────────
mcp:
server_name: "ai-memory"
server_version: "0.1.0"
# ── Personal Memory Modules ──────────────────────────────
personal:
enabled: true
data_dir: "~/.ai-memory/personal"
```
### Data Directory
All data is stored under `~/.ai-memory/` by default:
```
~/.ai-memory/
memory.db # Core memory SQLite database (WAL mode)
chroma/ # ChromaDB vector store
models/ # Cached local embedding model
config.yaml # Optional config override
vault/ # Encryption vault metadata
.vault_autokey # Auto-mode Fernet key (0600 permissions)
personal/ # Personal memory module data
habits.db
experiences.db
knowledge.db
lifestyle.db
accounts.db
cross_links.db # Cross-module link graph
```
---
## Usage -- MCP Server Setup
The server communicates over **stdio transport** using the Model Context Protocol. It works with any MCP-compatible client.
### Cursor
Add the following to your Cursor MCP configuration (Settings > MCP or `.cursor/mcp.json`):
```json
{
"mcpServers": {
"ai-memory": {
"command": "ai-memory-mcp",
"args": []
}
}
}
```
If you installed in a virtual environment, use the full path:
```json
{
"mcpServers": {
"ai-memory": {
"command": "/path/to/venv/bin/ai-memory-mcp",
"args": []
}
}
}
```
### Claude Desktop
Add to your Claude Desktop configuration file (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"ai-memory": {
"command": "ai-memory-mcp",
"args": []
}
}
}
```
### Direct invocation
```bash
ai-memory-mcp
```
The server reads JSON-RPC 2.0 requests from stdin and writes responses to stdout. If the `mcp` Python package is not installed, a minimal stdio JSON-RPC fallback handler is used automatically.
### Config file discovery
On startup, the server searches for `config.yaml` in this order:
1. Current working directory (`./config.yaml`)
2. Project root (relative to the package source)
3. User home (`~/.ai-memory/config.yaml`)
If none is found, built-in defaults are used.
---
## MCP Tools Reference
The server exposes **24 MCP tools** in total: 9 core memory tools and 15 personal memory tools.
### Core Memory Tools (9)
| Tool | Description | Key Parameters |
|---|---|---|
| `memory_search` | Search memories through the full L1->L2->L3 retrieval pipeline. Returns a formatted context string within the token budget. | `query` (required), `project_id`, `max_tokens` |
| `memory_add` | Add a new memory with automatic embedding, tag extraction, and write-time deduplication. Merges if similarity exceeds the threshold. | `content` (required), `memory_type`, `tags`, `importance`, `project_id` |
| `memory_update` | Update an existing memory's content. Regenerates the embedding and refreshes the vector store. | `memory_id` (required), `content` |
| `memory_delete` | Delete memories by ID or by tags. Soft-deletes in SQLite and removes from the vector store. | `memory_id`, `tags` |
| `memory_list` | List memories with optional type and tag filters. Returns formatted entries with importance, access count, and tags. | `memory_type`, `tags`, `project_id`, `limit` |
| `memory_stats` | Return memory statistics: total count, type distribution, and cache hit rate. | `project_id` |
| `memory_confirm_usage` | Confirm that specific memories were used in the current session. Increments adoption count and strengthens Hebbian connections between co-activated memories. | `memory_ids` (required), `session_id` |
| `memory_feedback` | Submit feedback for a memory. Updates feedback history and recomputes the reinforcement weight. | `memory_id` (required), `score` (required: -1, 0, +1), `comment` |
| `memory_audit_report` | Generate a memory audit report with redundancy analysis, feedback summary, graph statistics, and a composite health score (0--1). | `project_id` |
**Memory types:** `semantic`, `episodic`, `procedural`, `working`
### Personal Memory Tools (15)
#### Unified / Cross-Module (3)
| Tool | Description | Key Parameters |
|---|---|---|
| `personal_search` | Search across all personal memory modules (accounts, habits, experiences, knowledge, lifestyle). Returns matching results from each. | `query` (required), `limit_per_module` |
| `personal_overview` | Get an aggregate overview of all personal memory data: total counts per module and cross-module link count. | *(none)* |
| `personal_due_items` | Get all items needing attention: overdue habits, due knowledge reviews, and high decay risk cards. | *(none)* |
#### Account Vault (4)
| Tool | Description | Key Parameters |
|---|---|---|
| `account_search` | Search stored accounts by platform, username, or email. Returns masked results (no passwords). | `query` (required), `limit` |
| `account_get` | Get detailed information for a specific account by ID. Returns masked data. | `account_id` (required) |
| `account_get_password` | Decrypt and return the plaintext password for a specific account. Use with caution. | `account_id` (required) |
| `account_add` | Add a new account with an encrypted password. The password is encrypted before storage and never stored in plaintext. | `platform` (required), `username` (required), `password` (required), `category`, `email`, `url`, `notes`, `tags`, `password_hint`, `two_factor_enabled` |
**Account categories:** `email`, `social`, `cloud`, `developer`, `finance`, `shopping`, `entertainment`, `work`, `education`, `government`, `other`
#### Habit Tracking (3)
| Tool | Description | Key Parameters |
|---|---|---|
| `habit_list` | List all habits with optional category filter and favorites-only mode. | `category`, `favorites_only` |
| `habit_checkin` | Check in a habit for today or a specified date. Updates streak counters automatically. | `habit_id` (required), `date`, `note`, `mood` |
| `habit_overdue` | Get all habits that are overdue for check-in. | *(none)* |
**Habit frequencies:** `daily`, `weekly`, `mon_fri`, `specific_days`, `every_n_days`, `monthly`
#### Knowledge Management (3)
| Tool | Description | Key Parameters |
|---|---|---|
| `knowledge_search` | Search knowledge cards by title, content, or tags. | `query` (required), `limit` |
| `knowledge_due` | Get knowledge cards that are due for review (SM-2 algorithm). | `limit` |
| `knowledge_review` | Review a knowledge card, updating its SM-2 schedule. | `card_id` (required), `mastery_after` (required), `notes` |
**Mastery levels:** `aware`, `familiar`, `proficient`, `mastered`
#### Experience Recording (2)
| Tool | Description | Key Parameters |
|---|---|---|
| `experience_search` | Search experiences by title, description, or tags. | `query` (required), `limit` |
| `experience_add` | Record a new experience entry with optional lessons and tags. | `title` (required), `description`, `category`, `sentiment`, `importance`, `lessons`, `tags` |
**Experience categories:** `career`, `relationship`, `travel`, `education`, `health`, `finance`, `creativity`, `failure`, `success`, `life_lesson`, `conflict`, `discovery`, `growth`, `loss`, `transition`, `other`
---
## Security
### Account Vault Encryption
The account vault uses a two-layer encryption scheme:
1. **Key Derivation:** The master encryption key is derived using **Argon2id** (RFC 9106) with 2 GiB memory cost, 4 lanes, and 1 iteration. If Argon2id is unavailable, **PBKDF2-HMAC-SHA256** with 1,200,000 iterations is used as a fallback. The salt is 16 bytes of cryptographic random.
2. **Symmetric Encryption:** The derived key is used with **Fernet** (AES-128-CBC + HMAC-SHA256), which provides authenticated encryption -- tampering with ciphertext is detected on decryption.
3. **Key Storage Modes:**
- **Auto mode (default):** A random Fernet key is generated on first use and stored in a permission-protected file (0600). No user password is required. Suitable for local-only plugins where OS file permissions provide the first line of defense.
- **Password mode (optional):** The user sets a master password, validated via Argon2id key derivation against a stored verification token. More secure for shared devices. Users can upgrade from auto mode at any time via `upgrade_to_password()`.
4. **Sensitive fields are never stored in plaintext.** Passwords, security answers, API keys, and recovery codes are encrypted as Fernet tokens. Non-sensitive fields (platform, category, tags) remain in plaintext for searchability.
### Data Masking
All display output from personal memory tools applies configurable masking:
| Data Type | Masking Example |
|---|---|
| Passwords | Always fully masked (`--------`) |
| Emails | `u***@example.com` (first char + domain visible) |
| Phone numbers | `138****8888` (first 3 + last 4 digits) |
| API keys | `sk-****...****ab2f` (first 4 + last 4 characters) |
| Credit cards | `**** **** **** 1234` (last 4 digits only) |
| Usernames | `user***` (partial masking based on length) |
| ID cards | `110***********1234` (first 3 + last 4 digits) |
Three masking levels are available: `full` (complete masking), `partial` (default, shows some characters), and `none` (no masking, use with caution).
### Privacy Levels
All personal data entries carry a `privacy_level` field for access control:
- `public` -- Shareable
- `personal` -- Default for habits, experiences, knowledge, preferences
- `sensitive` -- Default for contacts and addresses
- `highly_sensitive` -- Reserved for the most sensitive data
### File Permissions
Vault directory and key files are created with restrictive permissions:
- Vault directory: `0700` (owner only)
- Key/metadata files: `0600` (owner read/write only)
---
## Development
### Setup
```bash
git clone https://github.com/sscctv/mcp-memory-suite.git
cd mcp-memory-suite
pip install -e ".[dev]"
```
### Running Tests
The project includes 1204 tests with 91% code coverage.
```bash
# Run all tests
pytest
# Run with verbose output
pytest -v
# Run a specific test file
pytest tests/test_core.py
```
### Test Organization
| Test File | Coverage Area |
|---|---|
| `tests/test_core.py` | Core memory system: storage, retrieval, weights, Hebbian, governance, bootstrap |
| `tests/test_personal.py` | Personal memory stores: habits, experiences, knowledge, lifestyle |
| `tests/test_personal_mcp.py` | Personal MCP tool dispatcher and tool definitions |
| `tests/test_auto_crypto.py` | Vault auto-mode encryption/decryption |
| `tests/test_habits.py` | Habit tracking: check-ins, streaks, overdue detection |
| `tests/test_experiences.py` | Experience recording: categories, sentiments, reflections |
| `tests/test_knowledge.py` | Knowledge management: SM-2 scheduling, decay risk |
| `tests/test_lifestyle.py` | Lifestyle: preferences, routines, contacts, addresses |
| `tests/test_manager.py` | PersonalMemoryManager: cross-module linking, unified search |
| `tests/test_cli.py` / `test_cli_extended.py` | CLI commands: all subcommands and edge cases |
| `tests/test_mcp_extended.py` | Extended MCP server tool testing |
| `tests/test_review.py` | Scheduled review system: daily/weekly reports, scheduler |
| `tests/test_notifications.py` | Notification and reminder system |
| `tests/test_backup.py` | Backup and recovery system |
| `tests/test_sync.py` | Multi-device synchronization |
| `tests/test_import_export.py` / `test_import_export_extended.py` | Data import/export |
| `tests/test_web_server.py` | Web UI server |
| `tests/test_coverage_gaps.py` | Edge cases and coverage gap fills |
### Coverage Report
```bash
# Generate coverage report
pytest --cov=memory_plugin --cov-report=html
# Open htmlcov/index.html in a browser
```
### Benchmarks
Benchmark scripts are available in the `benchmarks/` directory:
```bash
python benchmarks/benchmark_retrieval.py # Retrieval pipeline performance
python benchmarks/benchmark_performance.py # Overall system performance
```
Results are saved to `benchmarks/retrieval_report.json` and `benchmarks/performance_report.json`.
---
## Project Structure
```
ai-memory-mcp/
|-- config.yaml # Main configuration file
|-- pyproject.toml # Package metadata, dependencies, entry points
|-- src/
| `-- memory_plugin/
| |-- __init__.py
| |-- mcp_server.py # MCP server: 9 core tool handlers, lifecycle
| |-- personal_mcp_tools.py # 15 personal tool definitions + dispatcher
| |-- config.py # Configuration dataclasses, YAML loading
| |-- models.py # MemoryEntry, Feedback, Provenance, SearchResult
| |-- embedding.py # Embedding provider (API / local / hash fallback)
| |-- weights.py # Three-factor reinforcement weight calculator
| |-- hebbian.py # Hebbian connection weight updater
| |-- governance.py # Audit, redundancy/contradiction detection, health score
| |-- lifecycle.py # Decay, compression, forgetting, archiving
| |-- bootstrap.py # Codebase scanner for cold-start memory extraction
| |-- utils.py # Cosine similarity, token counting, tag extraction
| |-- retrieval/
| | |-- __init__.py
| | |-- l1_direct.py # L1: vector + BM25 parallel recall, fusion
| | |-- l2_expansion.py # L2: Personalized PageRank spreading activation
| | |-- l3_context.py # L3: weighted scoring, token-budget packing, formatting
| | `-- fusion.py # Multi-channel score fusion + semantic deduplication
| |-- storage/
| | |-- __init__.py
| | |-- sqlite_store.py # SQLite storage with WAL mode
| | |-- vector_store.py # ChromaDB vector storage
| | `-- cache.py # LRU hot cache with TTL
| `-- personal/
| |-- __init__.py
| |-- manager.py # PersonalMemoryManager: unified facade, cross-module linking
| |-- crypto.py # VaultCrypto: Argon2id + Fernet encryption
| |-- masking.py # DataMasking: PII masking utilities
| |-- password_utils.py # Password generator + strength evaluator
| |-- shared_types.py # PrivacyLevel, ModuleType, LinkType enums
| |-- account_models.py # AccountEntry, SecurityQuestion, APIKeyEntry
| |-- account_store.py # AccountStore: encrypted CRUD operations
| |-- habits_models.py # Habit, HabitCheckIn, frequency/category enums
| |-- habits_store.py # HabitStore: check-ins, streaks, overdue detection
| |-- experiences_models.py # Experience, EmotionPoint, GrowthOutcome, Reflection
| |-- experiences_store.py # ExperienceStore: CRUD, search, stats
| |-- knowledge_models.py # KnowledgeCard, ReviewRecord, SM-2 types
| |-- knowledge_store.py # KnowledgeStore: SM-2 scheduling, decay risk
| |-- lifestyle_models.py # Preference, Routine, Contact, Address
| |-- lifestyle_store.py # LifestyleStore: multi-entity storage
| |-- backup.py # Backup and recovery with retention
| |-- notifications.py # Reminder engine: habits, reviews, expiries
| |-- review.py # Scheduled review: daily/weekly reports
| |-- sync.py # Multi-device sync via JSON snapshots
| `-- web_server.py # Web UI server for browser access
|-- tests/ # 1204 tests, 91% coverage
| |-- conftest.py
| |-- test_core.py
| |-- test_personal.py
| |-- test_personal_mcp.py
| |-- test_auto_crypto.py
| |-- test_habits.py
| |-- test_experiences.py
| |-- test_knowledge.py
| |-- test_lifestyle.py
| |-- test_manager.py
| |-- test_cli.py
| |-- test_cli_extended.py
| |-- test_mcp_extended.py
| |-- test_review.py
| |-- test_notifications.py
| |-- test_backup.py
| |-- test_sync.py
| |-- test_import_export.py
| |-- test_import_export_extended.py
| |-- test_web_server.py
| `-- test_coverage_gaps.py
|-- benchmarks/ # Performance benchmarking scripts
|-- data/ # Runtime data (vault, databases)
`-- coverage.json # Coverage report data
```
---
## License
This project is licensed under the MIT License -- see the [LICENSE](LICENSE) file for details.
Maintenance
ActivitySlowing
ResponsivenessNo issues