Persistent, queryable memory for AI coding agents
Project description
Enyal
Persistent, queryable memory for AI coding agents.
Enyal gives AI agents like Claude Code durable context that survives session restarts. Every conversation becomes accumulated institutional knowledge—facts, preferences, decisions, and conventions that persist and grow.
Features
- Persistent Memory: Context survives restarts, crashes, and process termination
- Semantic Search: Find relevant context using natural language queries (384-dim embeddings via all-MiniLM-L6-v2)
- Hierarchical Scoping: Global → workspace → project → file context inheritance
- Fully Offline: Zero network calls during operation
- Cross-Platform: macOS (Intel + Apple Silicon) and Windows 10/11
- MCP Compatible: Works with Claude Code, Cursor, Windsurf, Kiro, and any MCP client
Quick Start
Get up and running in under 2 minutes:
1. Install
pip install enyal
2. Configure Your IDE
Create a configuration file for your IDE (all use the same format):
Claude Code — Create .mcp.json in your project root:
{
"mcpServers": {
"enyal": {
"command": "python",
"args": ["-m", "enyal.mcp"]
}
}
}
3. Start Using
You: Remember that this project uses pytest for all testing
Assistant: [calls enyal_remember] ✓ Stored context about testing framework
You: What testing framework should I use?
Assistant: [calls enyal_recall] Based on the stored context, this project uses pytest for all testing.
Installation
# Using uv (recommended)
uv add enyal
# Using pip
pip install enyal
# Using pipx (CLI only)
pipx install enyal
MCP Integration
Enyal works with any MCP-compatible client. Configuration is similar across platforms—only the file location differs.
Claude Code
File locations:
- Project:
.mcp.json(in project root) - User:
~/.claude/.mcp.json
Configuration:
{
"mcpServers": {
"enyal": {
"command": "python",
"args": ["-m", "enyal.mcp"],
"env": {
"ENYAL_DB_PATH": "~/.enyal/context.db"
}
}
}
}
CLI setup:
claude mcp add-json enyal '{"command":"python","args":["-m","enyal.mcp"]}'
Claude Desktop
File locations:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Configuration:
{
"mcpServers": {
"enyal": {
"command": "python",
"args": ["-m", "enyal.mcp"],
"env": {
"ENYAL_DB_PATH": "~/.enyal/context.db"
}
}
}
}
Cursor
File locations:
- Global:
~/.cursor/mcp.json - Project:
.cursor/mcp.json
Configuration:
{
"mcpServers": {
"enyal": {
"command": "python",
"args": ["-m", "enyal.mcp"],
"env": {
"ENYAL_DB_PATH": "~/.enyal/context.db"
}
}
}
}
UI setup: File → Preferences → Cursor Settings → MCP
Windsurf
File location: ~/.codeium/windsurf/mcp_config.json
Configuration:
{
"mcpServers": {
"enyal": {
"command": "python",
"args": ["-m", "enyal.mcp"],
"env": {
"ENYAL_DB_PATH": "~/.enyal/context.db"
}
}
}
}
UI setup: Windsurf Settings → Cascade → MCP, or use the Plugin Store
Kiro
File locations:
- Global:
~/.kiro/settings/mcp.json - Project:
.kiro/settings/mcp.json
Configuration:
{
"mcpServers": {
"enyal": {
"command": "python",
"args": ["-m", "enyal.mcp"],
"env": {
"ENYAL_DB_PATH": "~/.enyal/context.db"
},
"autoApprove": ["enyal_recall", "enyal_stats", "enyal_get"]
}
}
}
UI setup: Click the Kiro ghost tab → MCP Servers → "+"
See docs/INTEGRATIONS.md for detailed platform-specific guides.
Available Tools
| Tool | Description |
|---|---|
| enyal_remember | Store new context with metadata (facts, preferences, decisions, conventions, patterns) |
| enyal_recall | Semantic search for relevant context with filtering by scope and type |
| enyal_forget | Remove or deprecate context (soft-delete by default, hard-delete optional) |
| enyal_update | Update existing entries (content, confidence, tags) |
| enyal_get | Retrieve a specific entry by ID with full metadata |
| enyal_stats | Get usage statistics and health metrics |
Content Types
| Type | Use For | Example |
|---|---|---|
fact |
Objective information | "The database uses PostgreSQL 15" |
preference |
User/team preferences | "Prefer tabs over spaces" |
decision |
Recorded decisions | "Chose React over Vue for frontend" |
convention |
Coding standards | "All API endpoints follow REST naming" |
pattern |
Code patterns | "Error handling uses Result<T, E> pattern" |
Scope Levels
| Scope | Applies To | Example Path |
|---|---|---|
global |
All projects | (none) |
workspace |
Directory of projects | /Users/dev/projects |
project |
Single project | /Users/dev/myproject |
file |
Specific file | /Users/dev/myproject/src/auth.py |
CLI Usage
Enyal provides a command-line interface for direct interaction:
# Store context
enyal remember "Always use pytest for testing" --type convention --scope project
# Search context
enyal recall "testing framework" --limit 5
# Get entry details
enyal get <entry-id>
# View statistics
enyal stats
# Remove context
enyal forget <entry-id>
# Run MCP server
enyal serve --preload
Options:
--db PATH— Custom database path--json— Output in JSON format
See docs/CLI.md for complete CLI reference.
Python Library
from enyal.core.store import ContextStore
from enyal.core.retrieval import RetrievalEngine
from enyal.models.context import ContextType, ScopeLevel
# Initialize store
store = ContextStore("~/.enyal/context.db")
retrieval = RetrievalEngine(store)
# Remember something
entry_id = store.remember(
content="Always use pytest for testing in this project",
content_type=ContextType.CONVENTION,
scope_level=ScopeLevel.PROJECT,
scope_path="/Users/dev/myproject",
tags=["testing", "pytest"]
)
# Recall relevant context
results = retrieval.search(
query="how should I write tests?",
limit=5,
min_confidence=0.5
)
for result in results:
print(f"{result.score:.2f}: {result.entry.content}")
# Update context
store.update(entry_id, confidence=0.9, tags=["testing", "pytest", "unit-tests"])
# Get specific entry
entry = store.get(entry_id)
# Get statistics
stats = store.stats()
print(f"Total entries: {stats.total_entries}")
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
ENYAL_DB_PATH |
~/.enyal/context.db |
Database file location |
ENYAL_PRELOAD_MODEL |
false |
Pre-load embedding model at startup |
ENYAL_LOG_LEVEL |
INFO |
Logging level (DEBUG, INFO, WARNING, ERROR) |
Database Location
The default database is stored at ~/.enyal/context.db. This single SQLite file contains:
- All context entries and metadata
- Vector embeddings for semantic search
- Full-text search index
Troubleshooting
MCP Server Not Connecting
-
Verify Python path: Ensure
pythonresolves to Python 3.11+python --version -
Check installation: Verify enyal is installed
python -c "import enyal; print(enyal.__file__)"
-
Enable debug logging:
{ "mcpServers": { "enyal": { "command": "python", "args": ["-m", "enyal.mcp"], "env": { "ENYAL_LOG_LEVEL": "DEBUG" } } } }
-
Check server status:
- Claude Code:
/mcpcommand - Cursor: Settings → MCP → check status
- Windsurf: Cascade → Plugins
- Kiro: Ghost tab → MCP Servers
- Claude Code:
Slow First Query
The first query loads the embedding model (~80MB). This takes ~1-2 seconds. Subsequent queries are fast (~34ms).
To pre-load the model:
{
"env": {
"ENYAL_PRELOAD_MODEL": "true"
}
}
Database Locked Error
If you see "database is locked" errors, ensure only one MCP server instance is running per database file. Use different ENYAL_DB_PATH values for different projects if needed.
Permission Errors
On macOS/Linux, ensure the database directory exists and is writable:
mkdir -p ~/.enyal
chmod 755 ~/.enyal
Architecture
Enyal uses a unified SQLite database with:
- Relational storage for metadata and attributes
- sqlite-vec for vector similarity search (384-dim embeddings)
- FTS5 for keyword search
- WAL mode for concurrent access
See docs/ARCHITECTURE.md for detailed design decisions.
Development
# Clone repository
git clone https://github.com/seancorkum/enyal.git
cd enyal
# Install with dev dependencies
uv sync --all-extras
# Run tests
uv run pytest
# Type checking
uv run mypy src/enyal
# Linting
uv run ruff check src/enyal
Performance
Benchmarked on Intel Mac with Python 3.12:
| Metric | Target (p95) | Measured (p95) | Status |
|---|---|---|---|
| Cold start (model load + first query) | <2000ms | ~1500ms | ✓ |
| Warm query latency | <50ms | ~34ms | ✓ |
| Write latency | <50ms | ~34ms | ✓ |
| Concurrent reads (4 threads) | <150ms | ~85ms | ✓ |
| Memory (100k entries estimated) | <500MB | ~35MB | ✓ |
Embedding model: all-MiniLM-L6-v2 (22M params, 384 dimensions)
Run benchmarks:
uv run python benchmarks/benchmark_performance.py
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file enyal-0.1.1.tar.gz.
File metadata
- Download URL: enyal-0.1.1.tar.gz
- Upload date:
- Size: 208.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5e7d0dbd0a26018fc35af6ee0361f6d6d53d3a754a820831e5e8581a3956f94
|
|
| MD5 |
1723997613c2e22d840783e2128ecfe7
|
|
| BLAKE2b-256 |
d98eefb08759d98b495f70fbb53fdae0b86d12112e37ea4205ac503fa429ec5c
|
Provenance
The following attestation bundles were made for enyal-0.1.1.tar.gz:
Publisher:
publish.yml on S-Corkum/enyal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
enyal-0.1.1.tar.gz -
Subject digest:
f5e7d0dbd0a26018fc35af6ee0361f6d6d53d3a754a820831e5e8581a3956f94 - Sigstore transparency entry: 751109631
- Sigstore integration time:
-
Permalink:
S-Corkum/enyal@51da0119bb132ace3b0352a6eb71f592c53144b2 -
Branch / Tag:
refs/tags/0.1.1 - Owner: https://github.com/S-Corkum
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@51da0119bb132ace3b0352a6eb71f592c53144b2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file enyal-0.1.1-py3-none-any.whl.
File metadata
- Download URL: enyal-0.1.1-py3-none-any.whl
- Upload date:
- Size: 24.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
08496d84b4320c9a27351905311f5441a69a62a9b6ad6aa21e960e3955c71b22
|
|
| MD5 |
f13a4781202ae092505268b64f0f23a6
|
|
| BLAKE2b-256 |
2b565ce4685da583008ed12b10e35435c7d0ce344ea838150afe02bf02a70eca
|
Provenance
The following attestation bundles were made for enyal-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on S-Corkum/enyal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
enyal-0.1.1-py3-none-any.whl -
Subject digest:
08496d84b4320c9a27351905311f5441a69a62a9b6ad6aa21e960e3955c71b22 - Sigstore transparency entry: 751109709
- Sigstore integration time:
-
Permalink:
S-Corkum/enyal@51da0119bb132ace3b0352a6eb71f592c53144b2 -
Branch / Tag:
refs/tags/0.1.1 - Owner: https://github.com/S-Corkum
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@51da0119bb132ace3b0352a6eb71f592c53144b2 -
Trigger Event:
release
-
Statement type: