persistent-memory-core
Persistent Memory Core (PMC) is a memory layer for AI coding agents. It indexes your repositories into PostgreSQL + pgvector, records decisions, tasks, agent runs and lessons, and hands the right slice of that memory back to Claude Code, claude.ai, or any MCP client at the moment it is useful. GitHub stays the canonical source of truth; the database holds rebuildable derived memory.
Capabilities
- Hybrid retrieval — vector (HNSW) and full-text lanes fused with Reciprocal Rank Fusion, so exact identifiers and conceptual questions both land (ADR-030).
- Project memory — mission, milestones, prioritised tasks, decisions with rationale, issues, temporal facts, salience decay.
- Episodic lessons — lessons extracted from logged agent runs, embedded, deduplicated, reinforced when they help, injected before code.
- Ambient context — a compact context block for a prompt: lessons, relevant chunks, active tasks, recent decisions.
- Claude Code plugin — briefing on session start, ambient context on every
prompt, automatic flush before compaction and at session end, search /
brief / lessons / flush skills (
plugins/pmc-memory/). - MCP server — 33 tools over Streamable HTTP (
/mcp) and SSE, bearer or OAuth 2.0 + PKCE for claude.ai, deployed on Cloud Run. - Operations — Alembic migrations,
pmc doctor(connectivity, schema invariants, embedding dimensions, Cloud Run reachability), production runbook, CI on four Python versions against pgvector.
Quick start (local)
Prerequisites: Docker Desktop, uv, git.
git clone https://github.com/aress2525/persistent-memory-core.git
cd persistent-memory-core
docker compose up -d # PostgreSQL 16 + pgvector on port 5434
uv sync --all-extras # runtime extras + dev group (tests need them)
cp .env.example .env # add OPENAI_API_KEY / ANTHROPIC_API_KEY
uv run alembic upgrade head # schema
uv run pmc doctor # everything green?
uv run pmc add-project --key myproj --name "My project"
uv run pmc reindex --project myproj --repo-path ../my-repo
uv run pmc search --project myproj --query "connection pool"
scripts/bootstrap_local.{ps1,sh} does the same in one step. Without
OPENAI_API_KEY the mock embedding provider is used (deterministic vectors,
fine for tests, useless for real search). Full instructions, including
macOS/Windows notes and production bootstrap: docs/INSTALL.md.
Using it with Claude Code
Install the plugin once, then every checkout that has a .pmc.json (or a
registered git remote) gets memory automatically:
/plugin marketplace add aress2525/persistent-memory-core
/plugin install pmc-memory@pmc
Set PMC_API_KEY as a user-level environment variable (never in a settings
file). In a checkout of this repository, claude --plugin-dir plugins/pmc-memory
loads the plugin directly. What the hooks and skills do, and how to disable the
per-prompt context: plugins/pmc-memory/README.md
and the user manual.
For claude.ai, add the server as a custom connector at
https://pmc-mcp-318538827786.us-central1.run.app/mcp (OAuth is discovered
automatically): docs/CLAUDE_AI_SETUP.md.
CLI
The most used commands (uv run pmc --help lists all 60):
| Command | Purpose |
|---|---|
pmc doctor |
Health: database, schema invariants, embedding dims, Cloud Run |
pmc add-project / pmc projects |
Register and list projects |
pmc reindex --project K --repo-path P |
Index a repository (--changed-only for incremental) |
pmc search --project K --query Q |
Hybrid search with per-lane ranks |
pmc brief / pmc resume / pmc priorities |
Where you are and what is next |
pmc add-decision / pmc add-task |
Record memory by hand |
pmc flush --project K --text "…" |
Extract decisions and tasks from session text |
pmc lessons / pmc extract-lessons |
Episodic lessons |
pmc benchmark --project K --compare |
Recall@5, vector-only vs hybrid |
pmc set-remote |
Map a git remote to a project (used by the plugin) |
pmc export-md |
Obsidian-compatible Markdown export |
pmc oauth register |
Create an OAuth client for claude.ai |
Architecture
- docs/ARCHITECTURE.md — the full picture.
- Five schemas:
pmc_entity(projects, repositories, documents, chunks, decisions, tasks, issues, strategic memory, lessons),pmc_vector(embeddings),pmc_graph(edges),pmc_event(ingestion, retrieval, agent runs, mutations, sessions),pmc_policy(index policies). - Packages:
cli/(Click commands by topic),mcp_server/(tools/androutes/, tools run off the event loop behind a bounded limiter),retrieval/(vector, full-text, fusion, hydration, salience), plus single modules for stores, chunking, embedding providers, extraction, lessons. - Configuration is one typed
Settingsobject (config.py);os.environis banned elsewhere. Alembic owns the schema;sql/cloudsql_bootstrap.sqlonly creates the extension and schemas.
Decisions are recorded as ADRs in docs/ (ADR-004 and ADR-005 were never
written; numbering continues at 006). ADR-028 covers the uv toolchain and the
async tool model, ADR-029 the Claude Code plugin, ADR-030 hybrid retrieval.
Python API
from persistent_memory_core.db import get_connection
from persistent_memory_core.reindex import reindex
from persistent_memory_core.retrieval import hybrid_search
with get_connection() as conn:
reindex(conn, project_key="myproj", repo_path="../my-repo")
for r in hybrid_search(conn, "myproj", "get_embedding_provider", top_k=5):
print(r.file_path, r.metadata["vector_rank"], r.metadata["fts_rank"], round(r.score, 4))
Environment variables
Generated from Settings with uv run python scripts/dev/env_table.py. A
.env in the working directory beats the process environment (local
development is authoritative); production reads the injected environment.
Set PMC_DOTENV_OVERRIDE=0 to reverse that for one process.
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
postgresql+psycopg://pmc_user:…@localhost:5434/pmc_dev |
PostgreSQL connection string (local Docker by default) |
PMC_DB_READY_TIMEOUT |
15.0 |
Seconds the server waits for the database at startup |
PMC_TENANT_ID |
default |
Tenant scope for every query (single-tenant today) |
OPENAI_API_KEY |
(none) | Embeddings via OpenAI; omit for the mock provider |
EMBEDDING_MODEL |
text-embedding-3-small |
Embedding model name |
EMBEDDING_DIMS |
1536 |
Must equal the vector column width (pmc doctor checks) |
EMBEDDING_PROVIDER |
(none) | openai, local, mock, or blank for auto-selection |
EMBEDDING_BASE_URL |
(none) | OpenAI-compatible local embedding server (TEI, Ollama, ...) |
EMBEDDING_DEVICE |
cpu |
Device for the local provider |
EMBEDDING_BATCH_SIZE |
32 |
Batch size for the local provider |
ANTHROPIC_API_KEY |
(none) | Extraction and lesson models (Claude) |
PMC_EXTRACTION_MODEL |
claude-haiku-4-5-20251001 |
Model for pmc extract / session flush |
PMC_LESSON_MODEL |
claude-haiku-4-5-20251001 |
Model for lesson extraction |
PMC_API_KEY |
(none) | Operator bearer key for the MCP server (fail-closed) |
PMC_ALLOW_UNAUTHENTICATED |
False |
Local development only: run without a key |
PORT / MCP_PORT |
8080 |
Server port (PORT from Cloud Run wins over MCP_PORT) |
MCP_HOST |
0.0.0.0 |
Bind address |
MCP_ALLOWED_HOSTS |
(none) | Extra hosts for DNS-rebinding protection (comma-separated) |
PMC_CLOUD_RUN_URL |
https://pmc-mcp-318538827786.us-central1.run.app/health |
Health URL pmc doctor probes |
LOG_LEVEL |
INFO |
Python logging level |
PMC_SESSION_FILE |
~/.pmc/session.json |
Where the CLI keeps the active project |
SLACK_BOT_TOKEN |
(none) | Slack bot token (digest, /pmc command) |
SLACK_SIGNING_SECRET |
(none) | Slack request signing secret |
SLACK_DEFAULT_CHANNEL |
#pmc-updates |
Channel for the morning digest |
CLICKUP_API_TOKEN |
(none) | ClickUp two-way sync |
CLICKUP_WEBHOOK_SECRET |
(none) | HMAC secret for /webhooks/clickup; webhooks are refused until set |
NOTION_API_KEY |
(none) | Notion connector |
GOOGLE_SERVICE_ACCOUNT_KEY |
(none) | Google Drive connector (service-account JSON path) |
PMC_RRF_K |
60 |
RRF constant; lower = sharper rank decay |
PMC_VECTOR_WEIGHT |
1.0 |
Vector lane weight |
PMC_BM25_WEIGHT |
1.0 |
Full-text lane weight for identifier-like queries |
PMC_BM25_PROSE_WEIGHT |
0.15 |
Full-text lane weight for natural-language queries |
PMC_TOP_N_PER_SOURCE |
50 |
Candidates fetched per lane before fusion |
PMC_HNSW_EF_SEARCH |
100 |
Minimum hnsw.ef_search for vector queries |
Deploying
Production is Cloud Run (pmc-mcp) + Cloud SQL (pmc-postgres, database
pmc_prod) with secrets in Secret Manager. Migrations and rollouts go through
scripts/migrate_prod.ps1 (proxy → alembic upgrade head → pmc doctor →
optional -Deploy -ImageTag <sha>). The GitHub deploy.yml workflow only builds,
pushes and deploys the image behind a production approval gate (once Workload
Identity Federation is set up); it does not run migrations, so run the
runbook's migrate step first for any release that adds a schema revision. Details, rotation, backups and troubleshooting:
docs/ADMIN_MANUAL.md.
Project structure
persistent-memory-core/
.claude-plugin/marketplace.json # plugin marketplace (pmc-memory)
.pmc.json # this repo's project key (pmc-self)
alembic/versions/ # 0001 … 0015, the schema source of truth
plugins/pmc-memory/ # Claude Code plugin: .mcp.json, hooks/, skills/, templates/
scripts/ # bootstrap, proxy, secrets, migrate_prod.ps1, dev/ probes
sql/cloudsql_bootstrap.sql # extension + schemas only
src/persistent_memory_core/
config.py # Settings (pydantic-settings), the only env reader
cli/ # pmc command groups
mcp_server/ # server.py, guard.py, readiness.py, tools/, routes/
retrieval/ # vector.py, fts.py, fusion.py, hydrate.py, hybrid.py, salience.py
llm.py # structured-output LLM calls (Anthropic)
embedding_provider.py # OpenAI / local / mock providers
entity_store.py, vector_store.py, graph_store.py, event_store.py
reindex.py, chunking.py, github_ingest.py, policies.py
extraction.py, episodic_rl.py, ambient.py, executive_cues.py, benchmarks.py, doctor.py
integrations/, connectors/
tests/ # 300+ tests; `db` marker needs Docker pgvector
docs/ # manuals, architecture, ADRs, modernization plan
Documentation
- Installation · User manual · Admin manual
- Architecture · Multi-project · Multi-machine · Security · HIPAA · Data retention
- Modernization plan and status · Changelog
License
MIT
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 persistent_memory_core-1.1.0.tar.gz.
File metadata
- Download URL: persistent_memory_core-1.1.0.tar.gz
- Upload date:
- Size: 168.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd2ab98178841961dbc8572d9571616f018911342ad1487b1964921eed61ed03
|
|
| MD5 |
5b3e5f245475fe5f54b8c94bb3dbbbcf
|
|
| BLAKE2b-256 |
d051710986f11d83372a1b49ab43ae1542d6037b265e1e53ce7db47917bef767
|
Provenance
The following attestation bundles were made for persistent_memory_core-1.1.0.tar.gz:
Publisher:
publish.yml on aress2525/persistent-memory-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
persistent_memory_core-1.1.0.tar.gz -
Subject digest:
fd2ab98178841961dbc8572d9571616f018911342ad1487b1964921eed61ed03 - Sigstore transparency entry: 2795686098
- Sigstore integration time:
-
Permalink:
aress2525/persistent-memory-core@9b5039c7fee5efeec146f9b7da9104a8917f1d43 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/aress2525
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9b5039c7fee5efeec146f9b7da9104a8917f1d43 -
Trigger Event:
push
-
Statement type:
File details
Details for the file persistent_memory_core-1.1.0-py3-none-any.whl.
File metadata
- Download URL: persistent_memory_core-1.1.0-py3-none-any.whl
- Upload date:
- Size: 183.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
21374bc688785c28e35f5ba6a5b1b98d0856c27e1a738d566d43e6070ad11fee
|
|
| MD5 |
6c39a3e398bc5ab3f81d14c689c57572
|
|
| BLAKE2b-256 |
00d87cb811008b6bbc79a54e442537b7f90bb878444e119b11b6861116ae107d
|
Provenance
The following attestation bundles were made for persistent_memory_core-1.1.0-py3-none-any.whl:
Publisher:
publish.yml on aress2525/persistent-memory-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
persistent_memory_core-1.1.0-py3-none-any.whl -
Subject digest:
21374bc688785c28e35f5ba6a5b1b98d0856c27e1a738d566d43e6070ad11fee - Sigstore transparency entry: 2795686159
- Sigstore integration time:
-
Permalink:
aress2525/persistent-memory-core@9b5039c7fee5efeec146f9b7da9104a8917f1d43 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/aress2525
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9b5039c7fee5efeec146f9b7da9104a8917f1d43 -
Trigger Event:
push
-
Statement type: